ApiPlatform serialize exception - api-platform.com

I use ApiPlatform and my domain (DDD) can throw an exception like
final class QuotaExceededException extends \Exception
{
public function __construct(
private int $cost,
private int $quotaLeft,
string $message
) {
parent::__construct($message);
}
public function getCost(): int
{
return $this->cost;
}
public function getQuotaLeft(): int
{
return $this->quotaLeft;
}
}
I found how to customize the http code for that exception but i would like to return also a payload like
{
"type": "https:\/\/tools.ietf.org\/html\/rfc2616#section-10",
"title": "An error occurred",
"detail": "The message thrown",
"cost": 10,
"quotaLeft": 2
}
I tried to implement specific Normalizer (QuotaExceededExceptionNormalizer) but it seems that each exceptions thrown are converted to FlattenException which keep only the exception message and cost and quotaLeft informations are lost.
Have you any idea ?

Related

Getting invalid SpanContext in mongo extension for java OpenTelemetry automatic instrumentation

I am writing an extension of OpenTelemetry to input traceId as comment in mongo query. For this I have put an Advice on the find method in com.mongodb.client.MongoCollection. Inside the Advice when I call spanContext.isValid() it returns false, also spanId and traceId have all zeros in them.
MongoQueryInstrumentation.java
public class MongoQueryInstrumentation implements TypeInstrumentation {
#Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("com.mongodb.client.MongoCollection"));
}
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("find").and(takesArgument(0, Bson.class)).and(ElementMatchers.isPublic()),
AdvicesFind.class.getName());
}
#SuppressWarnings("unused")
public static class AdvicesFind {
#Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(#Advice.Return(readOnly = false) FindIterable<?> result) {
SpanContext spanContext = Java8BytecodeBridge.currentSpan().getSpanContext();
System.out.println("traceId:" + spanContext.getTraceId());
System.out.println("VALID :" + spanContext.isValid());
result.comment(spanContext.getTraceId());
}
}
}
MongoInstrumentationModule.java
#AutoService(InstrumentationModule.class)
public final class MongoInstrumentationModule extends InstrumentationModule {
public MongoInstrumentationModule() {
super("mongo-ext", "mongo-4.0");
}
#Override
public int order() {
return 1;
}
#Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
return hasClassesNamed("com.mongodb.internal.async.SingleResultCallback");
}
#Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new MongoQueryInstrumentation());
}
}
OUTPUT:
traceId:00000000000000000000000000000000
VALID :false
I am exporting the logs to zipkin, there db.statement is
{"find": "sampleCollection", "filter": {"title": "MongoDB"}, "comment": "00000000000000000000000000000000", "$db": "myDb", "lsid": {"id": {"$binary": {"base64": "nOpqMCwHRWe2h+qmVEgGIQ==", "subType": "04"}}}}
I tried doing similar thing in JDBC by adding a comment containing traceId and spanId and there it worked as expected. There I patched sendQueryString method of NativeProtocolInstrumentation.
There Java8BytecodeBridge.currentSpan().getSpanContext() returned a valid spanId but in mongo it does not.

Spring Can't recognise application.proerties file. Unable to display the configuration defines in property file

I'm trying to display my custom message while handling the exception. But I'm getting no response in the Postman. Here is code snippet that I've used
So, Here is my controller class method
#GetMapping(value="/customers/{customerId}")
public ResponseEntity<Customer> getCustomerById(#PathVariable Integer customerId) throws Exception
{
try {
Customer customer = customerService.getCustomer(customerId);
ResponseEntity<Customer> response = new ResponseEntity<Customer>(customer, HttpStatus.OK);
return response;
}
catch(Exception e)
{
throw new ResponseStatusException(HttpStatus.NOT_FOUND,environment.getProperty(e.getMessage()),e);
}
}
here is my service layer method that I'm calling from getCustomerById method -
#Service(value = "CustomerService")
public class CustomerServiceImpl implements CustomerService {
#Autowired
private CustomerDao customerDao;
#Override
public Customer getCustomer(Integer customerId) throws Exception {
Customer customer = customerDao.getCustomer(customerId);
if (customer == null) {
throw new Exception("Service.CUSTOMER_UNAVAILABLE");
}
return customer;
}
here is my property file
server.port=3557
Service.CUSTOMER_ALREADY_EXIST=Customer already present.Add customer with different attributes.
Service.CUSTOMER_UNAVAILABLE=Customer Details not found. Give valid customer details.
Postman response -
{
"timestamp": "2021-05-02T10:21:22.455+00:00",
"status": 500,
"error": "Internal Server Error",
"message": "",
"path": "/DemoBank/customers/8"
}
That's because you throw an exception instead of returning your custom response. Spring's exception always returns 500.
Change this line:
throw new ResponseStatusException(HttpStatus.NOT_FOUND,environment.getProperty(e.getMessage()),e);
to this:
return new ResponseEntity<>(environment.getProperty(e.getMessage()), HttpStatus.NOT_FOUND);

Getting a '404 Not Found' error, when testing serialised JSON-B output

I'm trying to build a simple JSON-B program, which converts a java object to a JSON string. The programme also uses the #JsonbProperty annotation on the pojo, to map an unmatched json property to the java object.
The string is then sent to a JMS queue, for a JMS consumer to pick up. I'm using a bash test script, to print the serialised json output in the console. The test script and .json files have been provided, and i'm just assuming they're correct.
When I run the bash script I'm getting a '404 Not Found' error, with no output in the console. I've attached the java code, the .json file, the test scripts and the error messages.
Any help would be greatly appreciated.
Thanks in advance..
Bash Script
curl -X POST -H "Content-Type: application/json" -d #order.json http://localhost:8080/hsports-catalog-jax/hsports/api/order
Bash Script Terminal Error
[21:27] ~/IdeaProjects/JEE8_Essential_Training/HSports_CatalogProject/jaxrs_module/src/resources Marc's Mac >> ./test.sh
<html><head><title>Error</title></head><body>404 - Not Found</body></html>[21:27] ~/IdeaProjects/JEE8_Essential_Training/HSports_CatalogProject/jaxrs_module/src/resources Marc's Mac >>
JAX-RS Endpoint
#RequestScoped
#Path("/order")
#Produces("application/json")
#Consumes("application/json")
public class OrderEndpoint {
// injects JMS producer from ejb module
#Inject
private JmsService jmsService;
// method for placing an order
// accepts Order object JAX-RS resource
// method will convert it to json
#POST
public void placeOrder(Order order) {
Jsonb jsonb = JsonbBuilder.create(); // json builder object
String json = jsonb.toJson(order); // converts order object to json representation
System.out.println(json);
jmsService.send(json);
}
}
JMS Producer
#ApplicationScoped
public class JmsService {
// injects JMS queue we want to send the message to
#Resource(mappedName = "java:/jms/queue/HsportsQueue") // JNDI name
private Queue hsportsQueue;
#Inject
#JMSConnectionFactory("java:/ConnectionFactory")
private JMSContext context;
// code that sends the message to the consumer
public void send(String message) {
try {
TextMessage textMessage = context.createTextMessage(message); // message object
context.createProducer().send(hsportsQueue, textMessage); // producer object
System.out.println("Message sent to JMS queue"); // console println
} catch (Exception e) {
e.printStackTrace();
}
}
}
JMS Consumer
#MessageDriven(
activationConfig = {
#ActivationConfigProperty(
propertyName = "destination", propertyValue = "/jms/queue/HsportsQueue"),
#ActivationConfigProperty(
propertyName = "destinationType", propertyValue = "javax.jms.Queue")
},
mappedName = "/jms/queue/HsportsQueue")
public class JmsConsumerBean implements javax.jms.MessageListener {
public JmsConsumerBean() {
}
// defines what the consumer does, when msg is received from jms queue
#Override
public void onMessage(Message message) {
System.out.println("Message received, JMS Consumer message-driven bean");
try {
System.out.println(message.getBody(String.class));
} catch (JMSException e) {
e.printStackTrace();
}
}
}
Java Pojo
public class Order {
private Long orderId;
private String storeName;
private Customer customer; // new class created
private List<InventoryItem> items;
public Long getOrderId() {
return orderId;
}
public void setOrderId(Long orderId) {
this.orderId = orderId;
}
public String getStoreName() {
return storeName;
}
public void setStoreName(String storeName) {
this.storeName = storeName;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public List<InventoryItem> getItems() {
return items;
}
// maps json-inventoryItems to java-item
#JsonbProperty("inventoryItems")
public void setItems(List<InventoryItem> items) {
this.items = items;
}
}
JSON File Content
{
"orderId": 1,
"storeName": "Franklin Park",
"customer": {
"customerId": 1,
"firstName": "Kevin",
"lastName": "Bowersox"
},
"inventoryItems": [
{
"inventoryItemId": 1,
"catalogItemId": 1,
"name": "Sneakers",
"quantity": 4
}
]
}
Ah! Problem solved. I had the wrong module name in the URI...idiot!

Response error format with manual validation in Spring Data Rest

When using Spring Data REST and JSR 303 Bean Validation I get a response like the following when there's a constraint violation:
{
"errors": [
{
"entity": "Empresa",
"property": "observacao",
"invalidValue": "",
"message": "O tamanho do campo deve ser entre 1 e 255"
}
]
}
But I'm trying to validate an object manually, and I would like to return the validation errors in the same format used by Spring Data Rest.
#DeleteMapping("/departamento/{id}")
public #ResponseBody
ResponseEntity<?> filtro(#PathVariable Long id){
Optional<Departamento> departamentoOpt = this.departamentoRepository.findById(id);
if (!departamentoOpt.isPresent()) {
return ResponseEntity.notFound().build();
}
Departamento departamento = departamentoOpt.get();
BindingResult errors = new BeanPropertyBindingResult(
departamento, "departamento");
this.validator.validate(departamento, errors, PreDeleteValidation.class);
if (errors.hasErrors()) {
// How to return a response in the same format used by SDR here?
}
return ResponseEntity.ok().build();
}
How can this be accomplished?
You can throw and Exception on validation failure and register a Spring MVC Controller Advice to catch this and transform it to something that meets your needs.
if (errors.hasErrors()) {
throw new org.springframework.web.bind.MethodArgumentNotValidException(
departamento, bindingResult)
}
The advice could look something like the below:
#ControllerAdvice
public class ErrorHandlingAdvice
{
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ValidationError processValidationError(MethodArgumentNotValidException ex)
{
ValidationError error = new ValidationError();
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError : fieldErrors)
{
error.addError(fieldError.getField(), fieldError.getDefaultMessage());
}
return error;
}
}
ValidationError is just a simple bean:
public class ValidationError
{
private final Map<String, List<String>> errors;
public ValidationError()
{
errors = new TreeMap<>();
}
public void addError(String field, String error)
{
if (!errors.containsKey(field))
{
errors.put(field, new ArrayList<String>());
}
errors.get(field).add(error);
}
public Map<String, List<String>> getErrors()
{
return errors;
}
}

Java XML, The number of formal and actual parameters differs, or an unwrapping conversion has failed

When requesting data from endpoint with accept: application/xml I keep getting the following error:
javax.xml.bind.MarshalException
- with linked exception: [Exception [EclipseLink-27] (Eclipse Persistence Services - 2.6.1.v20150916-55dc7c3):
org.eclipse.persistence.exceptions.DescriptorException Exception
Description: Trying to invoke the method [getSurveyid] on the object
[com.on24.ejb.mapping.SurveyQuestion]. The number of actual and
formal parameters differs, or an unwrapping conversion has failed.
Internal Exception: java.lang.IllegalArgumentException: object is not
an instance of declaring class Mapping:
org.eclipse.persistence.oxm.mappings.XMLDirectMapping[surveyid-->surveyid/text()]
Descriptor: XMLDescriptor(com.on24.ejb.mapping.Survey -->
[DatabaseTable(survey)])]
The response works fine when accept: application/json so I know it can't be a problem extracting the info from DB; I just haven't been able to solve this issue so any help will be greatly appreciated.
DTOs involved:
#XmlRootElement
#XmlType (propOrder={"surveyid",
"surveyquestions"})
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class Survey {
private Long surveyid;
private List<SurveyQuestion> surveyquestions;
public Survey(){}
public Long getSurveyid() {
return surveyid;
}
public void setSurveyid(Long surveyid) {
this.surveyid = surveyid;
}
#XmlElementWrapper(name="surveyquestionslist")
#XmlElement(name="surveyquestion")
public List<SurveyQuestion> getSurveyquestions() {
return surveyquestions;
}
public void setSurveyquestions(List<SurveyQuestion> surveyquestions) {
this.surveyquestions = surveyquestions;
}
}
And
#XmlRootElement
#XmlType (propOrder={"surveyquestionid",
"surveyquestion",
"surveyanswers"})
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class SurveyQuestion {
private Long surveyquestionid;
private String surveyquestion;
private List<String> surveyanswers;
public SurveyQuestion(){}
public Long getSurveyquestionid() {
return surveyquestionid;
}
public void setSurveyquestionid(Long surveyquestionid) {
this.surveyquestionid = surveyquestionid;
}
public String getSurveyquestion() {
return surveyquestion;
}
public void setSurveyquestion(String surveyquestion) {
this.surveyquestion = surveyquestion;
}
#XmlElementWrapper(name="surveyanswerslist")
#XmlElement(name="surveyanswer")
public List<String> getSurveyanswers() {
return surveyanswers;
}
public void setSurveyanswers(List<String> surveyanswers) {
this.surveyanswers = surveyanswers;
}
}
I've tried several thinks from refactoring to use XmlAccessType.PUBLIC_MEMBER, XmlAccessType.FIELD, XmlAccessType.PROPERTY but no success there.
I'd really like to understand why this error is generated. If more info is need I'll add it as per asked for, thanks.

Resources