Marshalling error on WebFault - spring

I have the following classes:
#WebFault(faultBean="com.myapp.FaultBean", name="MyWSException", targetNamespace = "exception.myapp.com")
public class MyWSException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 5911956366562576341L;
private FaultBean faultBean;
public MyWSException(String message, FaultBean faultBean) {
super(message);
this.faultBean = faultBean;
}
public MyWSException(String message, Throwable cause, FaultBean faultBean) {
super(message);
this.faultBean = faultBean;
}
public FaultBean getFaultInfo() {
return faultBean;
}
}
And this is my FaultBean:
#XmlType(name="WSFaultType", namespace = "exception.myapp.com")
#XmlRootElement(name="MyWSException")
#XmlAccessorType(XmlAccessType.FIELD)
public final class FaultBean {
#XmlElement(name="message", required=true)
private String message;
#XmlElement(name="code", required=true)
private String code;
public FaultBean(){
super();
}
public FaultBean( String message, String code) {
super();
this.message= message;
this.code = code;
}
public final String getMessage() {
return message;
}
public final void setMessage(String message) {
this.message= message;
}
public final String getCode() {
return code;
}
public final void setCode(String code) {
this.code = code;
}
}
Now I have two AOP classes, one to capture all my custom exceptions, and an additional one to capture Spring's exceptions when committing my transactions, in order to catch all unexpected errors of my persistence layer:
#Aspect
public class SpringExceptionAspect {
#Pointcut("execution(public * *(..))")
private final void anyPublicOperation() {
}
#Pointcut("execution(void org.springframework.transaction.PlatformTransactionManager.commit(*))")
public final void anyTransactionError() {
}
#AfterThrowing(pointcut = "anyPublicOperation() && anyTransactionError()", throwing = "error")
public void catchException(JoinPoint jp, Throwable error) {
FaultBean bean = new FaultBean(error.getMessage(), "ERRCODE");
throw new MyWSException("Persistence error:", bean);
}
}
#Aspect
public class CustomExceptionAspect{
#Pointcut("execution(public * *(..))")
private final void anyPublicOperation() {
}
#Pointcut("within(com.myapp.service..*)")
public final void anyServerMethod() {
}
#AfterThrowing(pointcut = "anyPublicOperation() && anyServerMethod()", throwing = "error")
public void catchException(JoinPoint jp, Throwable error) {
FaultBean bean = new FaultBean(error.getMessage(), "ERRCODE");
throw new MyWSException("Error:", bean);
}
}
Now when Exceptions get caught in the second Aspect (when I throw my custom exceptions), everything works and I get the expected soap:fault response. However when Spring's own Transaction exceptions are caught, I get this response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring
Marshalling Error: neither class com.myapp.FaultBean nor any of its superclasses are known to this conext</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
If they are the same classes, how can this be happening? Why is Jax-b able to marshall those from one point but not from the other one?

Related

EmptyResultDataAccessException when testing Spring controller

In my app, there is a controller, a service, a repo and a class. I am writing unit test to verify my PUT request. In postman, the put request works fine, however, when testing in JUnit test, it throws EmptyResultDataAccessException eror. Many other tests have the same problem and all of them require to find a specific entry in the repo by id. I think this is the problem. Please help me on this.
#Data
#Entity
public class ErrorMessage {
private #Id #GeneratedValue Long id;
private String message;
private int code;
public ErrorMessage() {
}
public ErrorMessage(int code, String message) {
this.code = code;
this.message = message;
}
}
#Repository
interface ErrorMessageRepository extends JpaRepository<ErrorMessage, Long> {
List<ErrorMessage> findByCode(int code);
}
#Service
public class ErrorMessageService {
#Autowired
ErrorMessageRepository repository;
#Transactional
public List<ErrorMessage> getAll()
{
return repository.findAll();
}
#Transactional(readOnly = true)
public Optional<ErrorMessage> getById(Long id)
{
return repository.findById(id);
}
#Transactional(readOnly = true)
public List<ErrorMessage> getByCode(int code)
{
return repository.findByCode(code);
}
#Transactional
public ErrorMessage saveOne(ErrorMessage messages)
{
return repository.save(messages);
}
#Transactional
public Optional<ErrorMessage> deleteById(long id)
{
Optional<ErrorMessage> em = repository.findById(id);
repository.deleteById(id);
return em;
}
#Transactional
public ErrorMessage updateById(long id, ErrorMessage newMessage)
{
ErrorMessage m = repository.findById(id).get();
m.setCode(newMessage.getCode());
m.setMessage(newMessage.getMessage());
repository.save(m);
return m;
}
}
class ErrorMessageController {
private static final Logger log = LoggerFactory.getLogger(ErrorMessageController.class);
#Autowired
ErrorMessageRepository repository;
#Autowired
private ErrorMessageService ems;
#GetMapping("/errormessages")
public List<ErrorMessage> getAll() {
return ems.getAll();
}
#GetMapping("/errormessagesbycode/{code}")
public List<ErrorMessage> getByCode(#PathVariable int code) {
return ems.getByCode(code);
}
#GetMapping("/errormessage/{id}")
ErrorMessage getById(#PathVariable Long id) {
return ems.getById(id)
.orElseThrow(() -> new MessageNotFoundException(id));
}
#PostMapping("/errormessage")
ErrorMessage newMessage(#RequestBody ErrorMessage newMessage) {
return ems.saveOne(newMessage);
}
#DeleteMapping("/errormessage/{id}")
Optional<ErrorMessage> deleteMessage(#PathVariable Long id) {
return ems.deleteById(id);
}
#PutMapping("/errormessage/{id}")
ErrorMessage updateMessage(#PathVariable Long id, #RequestBody ErrorMessage newMessage) {
return ems.updateById(id, newMessage);
}
}
#SpringBootTest
#AutoConfigureMockMvc
public class ErrorMessageTest {
private static ErrorMessage em, emId;
private static ObjectMapper mapper;
#Autowired
private MockMvc mockMvc;
#BeforeAll
public static void init() throws Exception {
mapper = new ObjectMapper();
em = new ErrorMessage(400, "bad request0");
emId = new ErrorMessage(400, "bad request0");
emId.setId(Long.valueOf(1));
}
#Test
void putMessage() throws Exception {
ErrorMessage modifiedMessage = new ErrorMessage(400, "modified");
this.mockMvc.perform(MockMvcRequestBuilders
.put("/errormessage/{id}", emId.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(modifiedMessage)))
.andExpect(status().isOk())
.andExpect(content().string(mapper.writeValueAsString(modifiedMessage)));
}
}
Try this
#Test
void putMessage() throws Exception {
ErrorMessage modifiedMessage = new ErrorMessage(400, "modified");
ErrorMessageService errorMessageService = Mockito.mock(ErrorMessageService.class);
Mockito.when(errorMessageService.updateById(Mockito.any(), Mockito.any())).thenReturn(modifiedMessage);
this.mockMvc.perform(MockMvcRequestBuilders
.put("/errormessage/{id}", emId.getId())
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(modifiedMessage)))
.andExpect(status().isOk())
.andExpect(content().string(mapper.writeValueAsString(modifiedMessage)));
}
I found out the bug. The order of the unit test is random. All i need to do is use #Order to ensure the order.

Throwing Custom Exception with HTTP Response From Spring Validator

I have implemented a custom Validator in Spring which is called inside an overridden Jackson de-serializer. If validation fails, I want the HTTP response code to be a 403 Forbidden as defined in my ControllerAdvice.
However, the response is always 400 Bad Request.
public class InterceptedDeserializer extends StdDeserializer<Object> implements ResolvableDeserializer
{
public InterceptedDeserializer(JsonDeserializer<?> defaultDeserializer)
{
super(Object.class);
this.defaultDeserializer = defaultDeserializer;
}
#Override public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, My403Exception
{
this.classFieldValidator = ServletUtils.findWebApplicationContext().getBean(ClassFieldValidator.class);
Object deserializedObject = defaultDeserializer.deserialize(jp, ctxt);
Errors errors = new BeanPropertyBindingResult(deserializedObject, deserializedObject.getClass().getName());
classFieldValidator.validate(deserializedObject, errors);
if(errors.hasErrors() || errors.hasFieldErrors()){
throw new My403Exception("No funny business");
}
return deserializedObject;
}
}
#ControllerAdvice
public class ValidationControllerAdvice {
private static final Logger log = LoggerFactory.getLogger(ValidationControllerAdvice.class);
private final StringWriter sw = new StringWriter();
#ResponseBody
#ExceptionHandler(My403Exception.class)
#ResponseStatus(HttpStatus.FORBIDDEN)
public ErrorResponse my403Exception(My403Exception e) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setErrorCode("my403");
errorResponse.setDescription(e.getMessage());
errorResponse.setMessage(e.getMessage());
e.printStackTrace(new PrintWriter(sw));
String eStackTrace = sw.toString();
log.error("My403 error message: " + e.getMessage() + "\nException Class:" + e.getClass() + "\nStack Trace:" + eStackTrace);
return errorResponse;
}
}
#ResponseStatus(value = HttpStatus.FORBIDDEN)
public class My403Exception extends RuntimeException{
private String message;
public My403Exception(String message) {
super(message);
this.message = message;
}
public My403Exception() {
}
#Override
public String getMessage() {
return message;
}
}
#ResponseStatus(HttpStatus.CREATED)
#RequestMapping(method = RequestMethod.POST, path = "/thing")
public void createmyThing(#RequestParam(value = "thing") String thing, #RequestBody() #Valid MyThing thing) throws My403Exception {
thingService.createThing(thing);
}

Spring Boot - Test - Validator: Invalid target for Validator

I'm getting the following error when I'm trying to run a test:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Invalid target for Validator [userCreateFormValidator bean]: com.ar.empresa.forms.UserCreateForm#15c3585
Caused by: java.lang.IllegalStateException: Invalid target for Validator [userCreateFormValidator bean]: com.ar.empresa.forms.UserCreateForm#15c3585
at org.springframework.validation.DataBinder.assertValidators(DataBinder.java:567)
at org.springframework.validation.DataBinder.addValidators(DataBinder.java:578)
at com.ar.empresa.controllers.UserController.initBinder(UserController.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
The code is:
Controller:
#Controller
public class UserController {
private UserService userService;
private UserCreateFormValidator userCreateFormValidator;
#Autowired
public UserController(UserService userService, UserCreateFormValidator userCreateFormValidator) {
this.userService = userService;
this.userCreateFormValidator = userCreateFormValidator;
}
#InitBinder("form")
public void initBinder(WebDataBinder binder) {
binder.addValidators(userCreateFormValidator);
}
#PreAuthorize("hasAuthority('ADMIN')")
#RequestMapping(value = "/user/create", method = RequestMethod.GET)
public ModelAndView getUserCreatePage() {
return new ModelAndView("user_create", "form", new UserCreateForm());
}
#PreAuthorize("hasAuthority('ADMIN')")
#RequestMapping(value = "/user/create", method = RequestMethod.POST)
public String handleUserCreateForm(#Valid #ModelAttribute("form") UserCreateForm form, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "user_create";
}
try {
userService.create(form);
} catch (DataIntegrityViolationException e) {
bindingResult.reject("email.exists", "Email already exists");
return "user_create";
}
return "redirect:/users";
}
}
Validator:
#Component
public class UserCreateFormValidator implements Validator {
private final UserService userService;
#Autowired
public UserCreateFormValidator(UserService userService) {
this.userService = userService;
}
#Override
public boolean supports(Class<?> clazz) {
return clazz.equals(UserCreateForm.class);
}
#Override
public void validate(Object target, Errors errors) {
UserCreateForm form = (UserCreateForm) target;
validatePasswords(errors, form);
validateEmail(errors, form);
}
private void validatePasswords(Errors errors, UserCreateForm form) {
if (!form.getPassword().equals(form.getPasswordRepeated())) {
errors.reject("password.no_match", "Passwords do not match");
}
}
private void validateEmail(Errors errors, UserCreateForm form) {
if (userService.getUserByEmail(form.getEmail()).isPresent()) {
errors.reject("email.exists", "User with this email already exists");
}
}
}
UserCreateForm:
public class UserCreateForm {
#NotEmpty
private String email = "";
#NotEmpty
private String password = "";
#NotEmpty
private String passwordRepeated = "";
#NotNull
private Role role = Role.USER;
public String getEmail() {
return email;
}
public String getPassword() {
return password;
}
public String getPasswordRepeated() {
return passwordRepeated;
}
public Role getRole() {
return role;
}
public void setEmail(String email) {
this.email = email;
}
public void setPassword(String password) {
this.password = password;
}
public void setPasswordRepeated(String passwordRepeated) {
this.passwordRepeated = passwordRepeated;
}
public void setRole(Role role) {
this.role = role;
}
}
Test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserControllerTest {
private MockMvc mockMvc;
private MediaType contentType = new MediaType(APPLICATION_JSON.getType(),
APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
#MockBean
private UserService userService;
#MockBean
private UserCreateFormValidator userCreateFormValidator;
#Autowired
FilterChainProxy springSecurityFilterChain;
#Before
public void setup() {
this.mockMvc = MockMvcBuilders.standaloneSetup(new UserController(userService,userCreateFormValidator)).apply(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain)).build();
}
#Test
#WithMockUser(username="user",
password="password",
roles="ADMIN")
public void homePage_authenticatedUser() throws Exception {
mockMvc.perform(get("/user/create"))
.andExpect(status().isOk())
.andExpect(view().name("user_create"));
}
}
I don't know why, because it is a GET method, so it don't have to validate it.
Thanks! :)
You got this exception because you didn't mock the behaviour of public boolean supports(Class<?> clazz) method on your userCreateFormValidator #Mockbean.
If you take a look at the code of org.springframework.validation.DataBinder.assertValidators(DataBinder.java) from the log you posted, you can find there how the validators are processed and how java.lang.IllegalStateException is thrown. In Spring 4.3.8, it looks like this
if(validator != null && this.getTarget() != null && !validator.supports(this.getTarget().getClass())) {
throw new IllegalStateException("Invalid target for Validator [" + validator + "]: " + this.getTarget());
}
You didn't mock supports method of the validator and returns false by default, causing Spring code above throw the IllegalStateException.
TLDR, just give me solution:
You have to mock supports method on your validator. Add following to #Before or #BeforeClass method.
when(requestValidatorMock.supports(any())).thenReturn(true);
I cant comment on the correct answer but his solution worked:
Here is what I had to do for this exact error.
//Imports
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
#MockBean
ApiValidationRouter apiValidationRouter;
#Before
public void beforeClass() throws Exception {
when(apiValidationRouter.supports(any())).thenReturn(true);
}

Spring, AspectJ: injection for deserialized objects

I need to put some object into ActiveMQ and then deserialize it. This object contain transient field which should be initialized upon deserialization. According to this doc (http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-atconfigurable) it should work automatically in the Spring, but it doesn't. CloudBean is always null after the deserialization.
Here are my classes:
package core.utilities.handlers;
//imports...
#Component
#Configurable
public class InitialCopyHandler implements JobHandler {
private static final long serialVersionUID = 2617677940515776720L;
private static Logger logger = Logger.getLogger(InitialCopyHandler.class);
private String username;
private String password;
#Autowired private transient CloudBean cloudBean;
public InitialCopyHandler() {
}
public InitialCopyHandler( String username, String password ) {
this.username = username;
this.password = password;
}
#Override
public void handle() {
try {
logger.info("Copy from default to " + username);
String webCloudDataDir = cloudBean.getWebCloudDataDir();
Sardine defaultSardine = cloudBean.getDefaultSardine();
InputStream is = defaultSardine.get(webCloudDataDir + FILE_COUNTRIES_LIST);
cloudBean.getUserSardine(username,password).put(webCloudDataDir + FILE_COUNTRIES_LIST, is);
} catch (IOException e) {
e.printStackTrace();
}
}
}
CloudBean.java:
package core.domain.cloud;
//imports...
#Component
public class CloudBean {
protected Sardine defaultSardine;
protected Sardine userSardine;
protected String webCloudDataDir;
protected String webCloud;
protected static String defaultCar;
protected static String defaultCarPassword;
protected HttpHeaders headers;
#Autowired private ConfigurationFactory configuration;
public CloudBean() {
}
// init block
#PostConstruct
private void init() {
webCloudDataDir = configuration.getConfigurationValue(DEFAULT_CLOUD_WEBDAV_LOCATION) + DIR_CLOUD_DATA;
webCloud = configuration.getConfigurationValue(DEFAULT_CLOUD_LOCATION);
defaultCar = configuration.getConfigurationValue(DEFAULT_CLOUD_ACCOUNT);
defaultCarPassword = configuration.getConfigurationValue(DEFAULT_CLOUD_ACCOUNT_PASSWORD);
defaultSardine = SardineFactory.begin(defaultCar, defaultCarPassword);
defaultSardine.enableCompression();
headers = HttpUtilities.initHeaders(defaultCar,defaultCarPassword);
}
/**
* Initializes Sardine for the particular user
* #param username
* #param password
* #return Sardine
*/
public Sardine getUserSardine( String username, String password ) {
userSardine = SardineFactory.begin(username,password);
userSardine.enableCompression();
return userSardine;
}
public Sardine getDefaultSardine() {
return defaultSardine;
}
public String getWebCloudDataDir() {
return webCloudDataDir;
}
}
Excerpt from the Consumer.java (deserialization part):
#Override
public void onMessage(Message message) {
if( message instanceof ObjectMessage ) {
ObjectMessage objectMessage = (ObjectMessage)message;
try {
JobHandler msg = (JobHandler)objectMessage.getObject();
es.execute(new Runnable() {
#Override
public void run() {
msg.handle();
}
});
} catch (JMSException e) {
e.printStackTrace();
}
}
}
What is wrong?
In your code Spring is just not involved in the deserialization, so it cannot inject any dependencies.
In the linked spring documentation the pojos are created by a spring factory. You cannot use that approach because you create your instances by deserialization.
What you can do :
In your Consumer class get a bean factory injected :
#Autowired
AutowireCapableBeanFactory beanFactory;
after deserializing the object autowire it :
JobHandler msg = (JobHandler)objectMessage.getObject();
beanFactory.autowireBean(msg);
thats all.

Why i get java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut getLogging

I get this exception on the title. I give some code following:
#Component("student")
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void printSomething() {
// get logging with aop
System.out.println("student printed something");
// get logging with aop
}
}
#Aspect
#Component
public class StudentLogging {
private final static Logger logger = Logger.getLogger(StudentLogging.class.getName());
#Pointcut("execution(* aspectorientedprog.aopexample.Student.printSomething(..))")
private void getLogging() {
}
#Around("getLogging()")
public String aroundPrintSomething(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("before printing something");
Object o = joinPoint.proceed();
logger.info("after printing something");
return o.toString();
}
}
public class AspectStudentTest {
#Test
public void aspect_student_test() {
ApplicationContext context = new ClassPathXmlApplicationContext("aspect/aspect-conf.xml");
Student student = context.getBean("student", Student.class);
student.printSomething();
System.out.println();
}
}
my configuration file:
<context:annotation-config/>
<context:component-scan base-package="aspectorientedprog"/>
<aop:aspectj-autoproxy/>
I research something about this error, but all solutions are not worked, it was about AOP version. if i use only
#Around(execution("class"))
it is working truly but if i use the #Pointcut and #Around like above i got this problem...
thanks for your answers
Try with:
#Pointcut("execution(* aspectorientedprog.aopexample.Student.printSomething(..))")
public void getLogging() {
}

Resources