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

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

Related

How i can set header in spring-ws client

When i try to call a service using webserviceTemplate of spring i get:
Caused by:
org.springframework.ws.soap.client.SoapFaultClientException: Failed to
assert identity with UsernameToken.
THIS IS THE CLASS I USE TO MAKE THE HEADER
package it.ita.Client;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.client.core.WebServiceMessageCallback;
import org.springframework.ws.soap.saaj.SaajSoapMessage;
import javax.xml.soap.*;
public class TokenHeaderRequestCallback implements WebServiceMessageCallback {
private static final Logger logger = LoggerFactory.getLogger(TokenHeaderRequestCallback.class);
private String username;
private String password;
public TokenHeaderRequestCallback(String username, String password){
this.username = username;
this.password = password;
}
public void doWithMessage(WebServiceMessage message) {
try {
System.out.println(message.toString());
SaajSoapMessage saajSoapMessage = (SaajSoapMessage)message;
SOAPMessage soapMessage = saajSoapMessage.getSaajMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
System.out.println("prima ----> "+soapEnvelope.toString());
SOAPHeader soapHeader = soapEnvelope.getHeader();
Name headerElementName = soapEnvelope.createName(
"Security",
"wsse",
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
);
SOAPHeaderElement soapHeaderElement = soapHeader.addHeaderElement(headerElementName);
SOAPElement usernameTokenSOAPElement = soapHeaderElement.addChildElement("UsernameToken", "wsse");
SOAPElement userNameSOAPElement = usernameTokenSOAPElement.addChildElement("Username", "wsse");
logger.info(this.username);
userNameSOAPElement.addTextNode(this.username);
SOAPElement passwordSOAPElement = usernameTokenSOAPElement.addChildElement("Password", "wsse");
passwordSOAPElement.addTextNode(this.password);
soapMessage.saveChanges();
System.out.println("dopo ----> "+soapEnvelope.toString());
} catch (SOAPException soapException) {
throw new RuntimeException("TokenHeaderRequestCallback", soapException);
}
}
}
AND I CALL THE SERVICES IN THIS WAY:
JAXBElement res = (JAXBElement) getWebServiceTemplate().marshalSendAndReceive(request,new TokenHeaderRequestCallback("userfrab","hdgdhdgdhdg"));
I've googled but not able to find anything useful, can you give me some help?

Long Spring Framework JMS Client Timeout On Broker Restart

I have a test JMS client application that uses JmsTemplate to send, and DefaultMessageListenerContainer to receive.
The application sends a single message to a queue, and then reads that message from the queue. It does this once a second. Think of it as a "periodic ping" to the broker. My goal is to use this to detect and report the ability to "ping the broker". In this context when the broker is down, I want the ping to fail within a configurable time period (e.g. 2 seconds).
When the system is healthy the typical "ping time" takes about 96 msec.
I then kill and restart the broker and expect the client ping to fail, it does. However the call to convertAndSend takes a very long time to return (15 to 21 seconds). This is much greater than my goal of 2 seconds.
The 15 second timeout can be seen here:
Notice in particular the two exceptions that are caught. The first one (underlined in yellow) is caught by the DefaultMessageListenerContainer. The second one (underlined in red) is one thrown by convertAndSend (caught in pol, see TrackedBroker.poll method in code below).
Once the broker is back (up and running) the "pinger" application again periodically pings the broker in the "healthy system"fashion.
In my current implementation the DefaultMessageListenerContainer, and the JmsTemplate each use their own pooling connection factory. As the connectionFactory construction method (seen below) suggests, I have attempted to configure the factory to shorten the length of timeouts. It would seem that the DefaultMessageListenerContainer is blocking for 15 seconds.
I am looking for suggestions on how to shorten the ping failure timeout from 15 to 2.
I'd prefer to let spring do as much as possible in terms of connection lifecycle management. I only started to muck with things like timeouts, when I was trying to solve this problem. I've dug through spring JMS documentation on JMSTemplate and on DefaultMessageListenerContainer, however because the management of the connection lifecycle to a JMS broker is not simple, its difficult to understand exactly how to resolve this problem.
The following is the code that I am currently using:
TrackedBroker
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.UncategorizedJmsException;
import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.jms.connection.SingleConnectionFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.listener.DefaultMessageListenerContainer;
import org.springframework.jms.listener.MessageListenerContainer;
import org.springframework.util.ErrorHandler;
import org.springframework.util.backoff.FixedBackOff;
import javax.jms.*;
import java.util.Date;
public class TrackedBroker implements MessageListener, ErrorHandler {
private int POLL_ATTEMPT_FAIL_THRESHOLD = 3;
private boolean alive = false;
private String name;
private String description;
private int pollAttempt = POLL_ATTEMPT_FAIL_THRESHOLD;
private int sequenceNumber = 0;
JmsTemplate template;
MessageListenerContainer messageListenerContainer;
String url;
String user;
String password;
String brokerEndpoint;
private static final Logger log = LoggerFactory.getLogger(TrackedBroker.class);
public TrackedBroker(AppConfig.Broker b, String brokerEndpoint) {
this.name = b.getName();
this.user = b.getUser();
this.password = b.getPassword();
this.brokerEndpoint = brokerEndpoint;
this.description = b.getDescription();
String url = "tcp://"+b.getHost()+":"+b.getPort();
this.url = url;
}
private MessageListenerContainer listenerContainer(String url, String user, String pw, String brokerEndpoint) throws JMSException {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.initialize();
container.setConnectionFactory(connectionFactory(url, user, pw));
container.setDestinationName(brokerEndpoint);
container.setReceiveTimeout(1000);
//container.setConcurrency("4");
//container.setConcurrentConsumers(10);
FixedBackOff backoff = new FixedBackOff(200, FixedBackOff.UNLIMITED_ATTEMPTS);
container.setBackOff(backoff);
container.setRecoveryInterval(100);
container.setMessageListener(this);
container.setErrorHandler(this);
return container;
}
private ConnectionFactory connectionFactory(String url, String user, String pw) throws JMSException{
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setPreAcknowledge(true);
connectionFactory.setBlockOnAcknowledge(false);
connectionFactory.setBlockOnNonDurableSend(false);
connectionFactory.setCallTimeout(500);
connectionFactory.setCallFailoverTimeout(500);
connectionFactory.setClientFailureCheckPeriod(500);
connectionFactory.setCallFailoverTimeout(400);
//connectionFactory.setConnectionTTL(3000);
connectionFactory.setBrokerURL(url);
connectionFactory.setPassword(pw);
connectionFactory.setUser(user);
CachingConnectionFactory caching_connection_factory = new CachingConnectionFactory(connectionFactory);
return caching_connection_factory;
}
private JmsTemplate jmsTemplate(String url, String user, String pw) throws JMSException{
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(connectionFactory(url, user, pw));
template.setDeliveryPersistent(false);
//template.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
return template;
}
public void init() throws JMSException {
setTemplate(jmsTemplate(url, user, password));
setMessageListenerContainer(listenerContainer(url, user, password, brokerEndpoint));
getMessageListenerContainer().start();
}
public void sendMessage(JmsTemplate jmsTmpl, String message) {
String label1 = "Broker Convert & Send" + getName();
Date date1 = DeltaLogger.logStart(label1);
jmsTmpl.convertAndSend(brokerEndpoint, message);
DeltaLogger.logEnd(label1, date1);
}
public void poll() {
incrementPollAttempt();
incrementSequenceNumber();
String probeMsg = "Probe Broker " + getName() + " Yo " + getSequenceNumber();
try {
String label1 = "Broker Send" + getName();
Date date1 = DeltaLogger.logStart(label1);
sendMessage(getTemplate(), probeMsg);
DeltaLogger.logEnd(label1, date1);
} catch (UncategorizedJmsException e) {
log.error("JMS Exception " + e.getLocalizedMessage());
}
log.info("My current poll attempts " + getName() + ": " + getPollAttempt());
if(getPollAttempt() > POLL_ATTEMPT_FAIL_THRESHOLD) {
setAlive(false);
} else {
setAlive(true);
}
}
public boolean isAlive() {
return alive;
}
private void setAlive(boolean alive) {
this.alive = alive;
}
public String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
public int getPollAttempt() {
return pollAttempt;
}
private void setPollAttempt(int pollAttempt) {
this.pollAttempt = pollAttempt;
}
private void incrementPollAttempt() {
this.pollAttempt++;
}
private void clearPollAttempt() {
this.pollAttempt = 0;
}
public JmsTemplate getTemplate() {
return template;
}
private void setTemplate(JmsTemplate template) {
this.template = template;
}
private void setSequenceNumber(int sequenceNumber) {
this.sequenceNumber = sequenceNumber;
}
public int getSequenceNumber() {
return sequenceNumber;
}
private void incrementSequenceNumber() {
this.sequenceNumber++;
}
public MessageListenerContainer getMessageListenerContainer() {
return messageListenerContainer;
}
private void setMessageListenerContainer(MessageListenerContainer messageListenerContainer) {
this.messageListenerContainer = messageListenerContainer;
}
public String getDescription() {
return description;
}
private void setDescription(String description) {
this.description = description;
}
#Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
try {
String text = ((TextMessage) message).getText();
log.info("Received " + text);
clearPollAttempt();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
#Override
public void handleError(Throwable throwable) {
log.error("Error" + throwable.getLocalizedMessage());
}
}
I currently load the following configuration (configprops.json):
{
"brokers": [
{"name": "artemis0", "description": "tonga", "host": "10.93.97.202", "port": 32192, "user": "artemis", "password": "aaa"},
{"name": "artemis1", "description": "spain", "host": "10.93.103.236", "port": 32192, "user": "artemis", "password": "aaa"}
]
}
JsonPropertySourceFactory
import com.fasterxml.jackson.databind.ObjectMapper;
import com.saab.ims.smart_proxy.controller.ScheduledTasks;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.io.IOException;
import java.util.Map;
public class JsonPropertySourceFactory
implements PropertySourceFactory {
private static final Logger log = LoggerFactory.getLogger(JsonPropertySourceFactory.class);
#Override
public PropertySource<?> createPropertySource(
String name, EncodedResource resource)
throws IOException {
Map readValue = new ObjectMapper()
.readValue(resource.getInputStream(), Map.class);
return new MapPropertySource("json-property", readValue);
}
}
AppConfig
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
#Component
#PropertySource(
value = "classpath:configprops.json",
factory = JsonPropertySourceFactory.class)
#ConfigurationProperties
public class AppConfig {
private static final Logger log = LoggerFactory.getLogger(AppConfig.class);
private int port;
private boolean resend;
private String host;
ObjectMapper mapper = new ObjectMapper();
public int getPort() {
return port;
}
public void setResend(boolean resend) {
this.resend = resend;
}
public boolean isResend() {
return resend;
}
public void setPort(int port) {
this.port = port;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
private List<String> topics;
public List<String> getTopics() {
return topics;
}
public void setTopics(List<String> topics) {
this.topics = topics;
}
private List<Broker> brokers ;
public List<Broker> getBrokers() {
return mapper.convertValue(brokers,
new TypeReference<List<Broker>>(){});
}
public void setBrokers(List<Broker> brokers) {
log.info("Broker is " + brokers);
this.brokers = brokers;
}
public static class Broker {
private String name;
private String description;
private String host;
private int port;
private String user;
private String password;
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
}
ScheduledTasks
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.listener.MessageListenerContainer;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
#Component
public class ScheduledTasks {
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
private long probeBrokerCnt = 0;
private long probeProxyCnt = 0;
private ArrayList<TrackedBroker> trackedBrokerList = new ArrayList<TrackedBroker>();
#Autowired
DispatcherService dispatcherService;
#Autowired
HaProxyService haproxyService;
#Autowired
private AppConfig appConfig;
#PostConstruct
public void init() {
List<AppConfig.Broker> brokerList =appConfig.getBrokers();
for(int i = 0; i < brokerList.size(); i++) {
AppConfig.Broker b = brokerList.get(i);
log.info("My cool broker " + b.getName() +
" Host " + b.getHost() + " Port " + b.getPort() +
" User " + b.getUser() + " PW " + b.getPassword());
TrackedBroker tb = new TrackedBroker(b, "Q.Proxy.Probe");
try {
tb.init();
} catch (JMSException e) {
log.error("Failed to create JMS Template " + e);
}
trackedBrokerList.add(tb);
}
}
#Scheduled(fixedRate = 5000)
public void reportCurrentTime() {
log.info("The time is now {}", dateFormat.format(new Date()));
}
#Scheduled(fixedRate = 1000)
public void probeBroker() {
Date date1 = new Date();
log.info("Starting Probe ");
for(TrackedBroker tb : trackedBrokerList) {
tb.poll();
log.info("Broker " + tb.getName() + " Desc " + tb.getDescription() + " Alive " + Boolean.toString(tb.isAlive()));
}
Date date2 = new Date();
long difference = date2.getTime() - date1.getTime();
log.info("Ending Probe- Duration(seconds): " + Double.toString(difference/1000.0));
//dispatcherService.sendMessage(probeMsg);
}
}

Spring Boot : Custom Validation in Request Params

I want to validate one of the request parameters in my controller . The request parameter should be from one of the list of given values , if not , an error should be thrown . In the below code , I want the request param orderBy to be from the list of values present in #ValuesAllowed.
#RestController
#RequestMapping("/api/opportunity")
#Api(value = "Opportunity APIs")
#ValuesAllowed(propName = "orderBy", values = { "OpportunityCount", "OpportunityPublishedCount", "ApplicationCount",
"ApplicationsApprovedCount" })
public class OpportunityController {
#GetMapping("/vendors/list")
#ApiOperation(value = "Get all vendors")
public ResultWrapperDTO getVendorpage(#RequestParam(required = false) String term,
#RequestParam(required = false) Integer page, #RequestParam(required = false) Integer size,
#RequestParam(required = false) String orderBy, #RequestParam(required = false) String sortDir) {
I have written a custom bean validator but somehow this is not working . Even if am passing any random values for the query param , its not validating and throwing an error.
#Repeatable(ValuesAllowedMultiple.class)
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {ValuesAllowedValidator.class})
public #interface ValuesAllowed {
String message() default "Field value should be from list of ";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String propName();
String[] values();
}
public class ValuesAllowedValidator implements ConstraintValidator<ValuesAllowed, Object> {
private String propName;
private String message;
private String[] values;
#Override
public void initialize(ValuesAllowed requiredIfChecked) {
propName = requiredIfChecked.propName();
message = requiredIfChecked.message();
values = requiredIfChecked.values();
}
#Override
public boolean isValid(Object object, ConstraintValidatorContext context) {
Boolean valid = true;
try {
Object checkedValue = BeanUtils.getProperty(object, propName);
if (checkedValue != null) {
valid = Arrays.asList(values).contains(checkedValue.toString().toLowerCase());
}
if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message.concat(Arrays.toString(values)))
.addPropertyNode(propName).addConstraintViolation();
}
} catch (IllegalAccessException e) {
log.error("Accessor method is not available for class : {}, exception : {}", object.getClass().getName(), e);
return false;
} catch (NoSuchMethodException e) {
log.error("Field or method is not present on class : {}, exception : {}", object.getClass().getName(), e);
return false;
} catch (InvocationTargetException e) {
log.error("An exception occurred while accessing class : {}, exception : {}", object.getClass().getName(), e);
return false;
}
return valid;
}
}
Case 1: If the annotation ValuesAllowed is not triggered at all, it could be because of not annotating the controller with #Validated.
#Validated
#ValuesAllowed(propName = "orderBy", values = { "OpportunityCount", "OpportunityPublishedCount", "ApplicationCount", "ApplicationsApprovedCount" })
public class OpportunityController {
#GetMapping("/vendors/list")
public String getVendorpage(#RequestParam(required = false) String term,..{
}
Case 2: If it is triggered and throwing an error, it could be because of the BeanUtils.getProperty not resolving the properties and throwing exceptions.
If the above solutions do not work, you can try moving the annotation to the method level and update the Validator to use the list of valid values for the OrderBy parameter. This worked for me. Below is the sample code.
#RestController
#RequestMapping("/api/opportunity")
#Validated
public class OpportunityController {
#GetMapping("/vendors/list")
public String getVendorpage(#RequestParam(required = false) String term,
#RequestParam(required = false) Integer page, #RequestParam(required = false) Integer size,
#ValuesAllowed(propName = "orderBy", values = { "OpportunityCount", "OpportunityPublishedCount", "ApplicationCount",
"ApplicationsApprovedCount" }) #RequestParam(required = false) String orderBy, #RequestParam(required = false) String sortDir) {
return "success";
}
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = { ValuesAllowed.Validator.class })
public #interface ValuesAllowed {
String message() default "Field value should be from list of ";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String propName();
String[] values();
class Validator implements ConstraintValidator<ValuesAllowed, String> {
private String propName;
private String message;
private List<String> allowable;
#Override
public void initialize(ValuesAllowed requiredIfChecked) {
this.propName = requiredIfChecked.propName();
this.message = requiredIfChecked.message();
this.allowable = Arrays.asList(requiredIfChecked.values());
}
public boolean isValid(String value, ConstraintValidatorContext context) {
Boolean valid = value == null || this.allowable.contains(value);
if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message.concat(this.allowable.toString()))
.addPropertyNode(this.propName).addConstraintViolation();
}
return valid;
}
}
}
You would have to change few things for this validation to work.
Controller should be annotated with #Validated and #ValuesAllowed should annotate the target parameter in method.
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#Validated
#RestController
#RequestMapping("/api/opportunity")
public class OpportunityController {
#GetMapping("/vendors/list")
public String getVendorpage(
#RequestParam(required = false)
#ValuesAllowed(values = {
"OpportunityCount",
"OpportunityPublishedCount",
"ApplicationCount",
"ApplicationsApprovedCount"
}) String orderBy,
#RequestParam(required = false) String term,
#RequestParam(required = false) Integer page, #RequestParam(required = false) Integer size,
#RequestParam(required = false) String sortDir) {
return "OK";
}
}
#ValuesAllowed should target ElementType.PARAMETER and in this case you no longer need propName property because Spring will validate the desired param.
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {ValuesAllowedValidator.class})
public #interface ValuesAllowed {
String message() default "Field value should be from list of ";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] values();
}
Validator:
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.List;
public class ValuesAllowedValidator implements ConstraintValidator<ValuesAllowed, String> {
private List<String> expectedValues;
private String returnMessage;
#Override
public void initialize(ValuesAllowed requiredIfChecked) {
expectedValues = Arrays.asList(requiredIfChecked.values());
returnMessage = requiredIfChecked.message().concat(expectedValues.toString());
}
#Override
public boolean isValid(String testValue, ConstraintValidatorContext context) {
boolean valid = expectedValues.contains(testValue);
if (!valid) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(returnMessage)
.addConstraintViolation();
}
return valid;
}
}
But the code above returns HTTP 500 and pollutes logs with ugly stacktrace. To avoid it, you can put such #ExceptionHandler method in controller body (so it will be scoped only to this controller) and you gain control over HTTP status:
#ExceptionHandler(ConstraintViolationException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
String handleConstraintViolationException(ConstraintViolationException e) {
return "Validation error: " + e.getMessage();
}
... or you can put this method to the separate #ControllerAdvice class and have even more control over this validation like using it across all the controllers or only desired ones.
I found that I was missing this dependency after doing everything else. Regular validation steps were working but the custom validators didn't work until I added this to my pom.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Handler Goblal Exceptions Spring - add data when sending exception

I have a doubt about how to pass more data to throw an exception, I want to pass more data at the time of launching it, to put that data in the service response ..
I have an exception handler class labeled #ControllerAdvice in spring, but I don't know the best way to pass the data.
This is the code I have
throw new OcspException("Exception OCSP");
public class OcspException extends RuntimeException {
private static final long serialVersionUID = 1L;
public OcspException(String businessMessage) {
super(businessMessage);
}
public OcspException(String businessMessage, Throwable throwable) {
super(businessMessage, throwable);
}
}
#ExceptionHandler(OcspException.class)
public ResponseEntity<Object> exception(OcspException exception,HttpServletRequest request) {
ResponseException response = new ResponseException();
response.setCode("404");
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
I have the idea to do it, but I don't know if it is a good practice ... in the OcspException class to create attributes with their setter and getters, and create the constructor that receives this data, to then extract the data in exception controller
throw new OcspException("Exception OCSP","Hello");
public class OcspException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String m;
public OcspException(String businessMessage) {
super(businessMessage);
}
public OcspException(String businessMessage, Throwable throwable) {
super(businessMessage, throwable);
}
public OcspException(String businessMessage, String message) {
super(businessMessage);
setM(message);
}
public String getM() {
return m;
}
public void setM(String m) {
this.m = m;
}
}
#ExceptionHandler(OcspException.class)
public ResponseEntity<Object> exception(OcspException exception,HttpServletRequest request) {
ResponseException response = new ResponseException();
response.setCode("404");
response.setDetails(exception.getM() );
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
Try making an model called ErrorDetails which will hold a timestamp, message, and details.
It may look like this:
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class ErrorDetails {
private LocalDateTime timeStamp;
private String message;
private String details;
}
Here's a sample of what my custom exceptions usually look like:
#Data
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class OrderNotFoundException extends RuntimeException {
private final String message;
public OrderNotFoundException(String message) {
super(message);
this.message = message;
}
}
Then for the #ExceptionHandler:
#ExceptionHandler(OrderNotFoundException.class)
public ResponseEntity<ErrorDetails>
orderNotFoundException(OrderNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = ErrorDetails.builder()
.timeStamp(LocalDateTime.now())
.message(ex.getMessage())
.details(request.getDescription(false))
.build();
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
The error response for an order not found ends up being this:
{
"timeStamp": "2019-10-07T21:31:37.186",
"message": "Order with id 70 was not found.",
"details": "uri=/api/v1/order"
}
This way you can add whatever extra details in the ErrorDetails object. I hope that helps!

Apache cxf basic authentication

I have a running example of apache cxf but when I run this wsdl file provided by my code is not authenticated I don't know how to pass the username and password to soapui
The code is:
ORDER.JAVA
package demo.order;
public class Order {
private String customerID;
private String itemID;
private int qty;
private double price;
// Constructor
public Order() {
}
public String getCustomerID() {
return customerID;
}
public void setCustomerID(String customerID) {
this.customerID = customerID;
}
public String getItemID() {
return itemID;
}
public void setItemID(String itemID) {
this.itemID = itemID;
}
public int getQty() {
return qty;
}
public void setQty(int qty) {
this.qty = qty;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
package demo.order;
import javax.jws.WebService;
#WebService
public interface OrderProcess {
String processOrder(Order order);
}
package demo.order;
import javax.jws.WebService;
#org.apache.cxf.interceptor.InInterceptors (interceptors = {"demo.order.server.OrderProcessUserCredentialInterceptor" })
#WebService
public class OrderProcessImpl implements OrderProcess {
public String processOrder(Order order) {
System.out.println("Processing order...");
String orderID = validate(order);
return orderID;
}
/**
* Validates the order and returns the order ID
**/
private String validate(Order order) {
String custID = order.getCustomerID();
String itemID = order.getItemID();
int qty = order.getQty();
double price = order.getPrice();
if (custID != null && itemID != null && qty > 0 && price > 0.0) {
return "ORD1234";
}
return null;
}
}
_______________
package demo.order.client;
import demo.order.OrderProcess;
import demo.order.Order;
import org.apache.cxf.frontend.ClientProxy;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Client {
public Client() {
}
public static void main(String args[]) throws Exception {
ClassPathXmlApplicationContext context
= new ClassPathXmlApplicationContext(new String[] {"demo/order/client/client-beans.xml"});
OrderProcess client = (OrderProcess) context.getBean("orderClient");
OrderProcessClientHandler clientInterceptor = new OrderProcessClientHandler();
clientInterceptor.setUserName("John");
clientInterceptor.setPassword("password");
org.apache.cxf.endpoint.Client cxfClient = ClientProxy.getClient(client);
cxfClient.getOutInterceptors().add(clientInterceptor);
Order order = new Order();
order.setCustomerID("C001");
order.setItemID("I001");
order.setQty(100);
order.setPrice(200.00);
String orderID = client.processOrder(order);
String message = (orderID == null) ? "Order not approved" : "Order approved; order ID is " + orderID;
System.out.println(message);
}
}
_____________________
package demo.order.client;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class OrderProcessClientHandler extends AbstractSoapInterceptor {
public String userName;
public String password;
public OrderProcessClientHandler() {
super(Phase.WRITE);
addAfter(SoapPreProtocolOutInterceptor.class.getName());
}
public void handleMessage(SoapMessage message) throws Fault {
System.out.println("OrderProcessClientHandler handleMessage invoked");
DocumentBuilder builder = null;
try {
builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (ParserConfigurationException e) {
e.printStackTrace();
}
Document doc = builder.newDocument();
Element elementCredentials = doc.createElement("OrderCredentials");
Element elementUser = doc.createElement("username");
elementUser.setTextContent(getUserName());
Element elementPassword = doc.createElement("password");
elementPassword.setTextContent(getPassword());
elementCredentials.appendChild(elementUser);
elementCredentials.appendChild(elementPassword);
// Create Header object
QName qnameCredentials = new QName("OrderCredentials");
Header header = new Header(qnameCredentials, elementCredentials);
message.getHeaders().add(header);
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
_________________________
CLIENTBEAN.XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<jaxws:client id="orderClient" serviceClass="demo.order.OrderProcess" address="http://localhost:8080/OrderProcess" />
</beans>
_______________________
package demo.order.server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import demo.order.OrderProcess;
import demo.order.OrderProcessImpl;
public class OrderProcessServerStart {
public static void main(String[] args) throws Exception {
OrderProcess orderProcess = new OrderProcessImpl();
JaxWsServerFactoryBean server = new JaxWsServerFactoryBean();
server.setServiceBean(orderProcess);
server.setAddress("http://localhost:8787/OrderProcess");
server.create();
System.out.println("Server ready....");
Thread.sleep(5 * 60 * 1000);
System.out.println("Server exiting");
System.exit(0);
}
}
___________________________
package demo.order.server;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
public class OrderProcessUserCredentialInterceptor extends AbstractSoapInterceptor {
private String userName;
private String password;
public OrderProcessUserCredentialInterceptor() {
super(Phase.PRE_INVOKE);
}
public void handleMessage(SoapMessage message) throws Fault {
System.out.println("OrderProcessUserCredentialInterceptor handleMessage invoked");
QName qnameCredentials = new QName("OrderCredentials");
// Get header based on QNAME
if (message.hasHeader(qnameCredentials )) {
Header header = message.getHeader(qnameCredentials);
Element elementOrderCredential= (Element) header.getObject();
Node nodeUser = elementOrderCredential.getFirstChild();
Node nodePassword = elementOrderCredential.getLastChild();
if (nodeUser != null) {
userName = nodeUser.getTextContent();
}
if (nodePassword != null) {
password = nodePassword.getTextContent();
}
}
System.out.println("userName retrieved from SOAP Header is " + userName);
System.out.println("password retrieved from SOAP Header is " + password);
// Perform dummy validation for John
if ("John".equalsIgnoreCase(userName) && "password".equalsIgnoreCase(password)) {
System.out.println("Authentication successful for John");
} else {
throw new RuntimeException("Invalid user or password");
}
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
_______________
I have these files and the program compile succesfully and creting the wsdl file
but its not working on soapui means username and password are null when requsting through soapui,
please suggest me how to overcome this problem of basic authentication
and how to pass the usename and password to soap ui
For Basic authentication in SOAP UI, navigate to your Request, single click on it will display a panel in the left bottom corner. Your config for BASIC Authentication should be something like this:
Add your username and password to the appropriate fields.
For your client I see you are using Spring. So jaxws:client provides username and password attributes for authentication. You can add them like this:
<jaxws:client id="orderClient"
serviceClass="demo.order.OrderProcess"
address="http://localhost:8080/OrderProcess"
username="yourUsername"
password="yourPassword"/>

Resources