When i use DELETE in postman on adress http://localhost:8081/api/data/removedata/1
i get a message response saying:"Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead",
this is the java code
#DeleteMapping("/removedata/{id}")
public ResponseEntity deleteData(#PathVariable String id) {
long remInt = Long.parseLong(id);
Data dataRem = em.find(Data.class, remInt);
em.getTransaction().begin();
em.remove(dataRem);
em.getTransaction().commit();
return ResponseEntity.ok(new MessageResponse("Data removed"));
}
What am i doing wrong? ps im a novice still struggling with the basics.
This issue was resolved by adding #Transactional like so:
#Transactional
#DeleteMapping("/removedata/{id}")
public ResponseEntity deleteData(#PathVariable String id) {
long remInt = Long.parseLong(id);
Data dataRem = em.find(Data.class, remInt);
em.getTransaction().begin();
em.remove(dataRem);
em.getTransaction().commit();
return ResponseEntity.ok(new MessageResponse("Data removed"));
}
Related
I have created put API in spring boot Application, Put what is happening that it's returning 404 not found, This is my Controller :
#RestController
#RequestMapping("/customer")
public class CustomerController {
#Autowired
CustomerServiceImpl customerService;
#PutMapping("/customer/{id}")
public ResponseEntity<Customer> updateCustomer(#PathVariable(value = "id") int id, #RequestBody CustomerDTO customer) {
try{
Customer newCustomer = customerService.updateCustomer(customer);
return new ResponseEntity<>(newCustomer, HttpStatus.OK);
}catch (Exception exception){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}
and this is my service :
#Override
#Transactional
public Customer updateCustomer(CustomerDTO customer) {
if (cutomerRepository.findById(customer.getId()) != null) {
Customer persistenceCustomer = cutomerRepository.findById(customer.getId()).get();
if (customer.getEmail() != null) {
persistenceCustomer.setEmail(customer.getEmail());
;
}
if (customer.getMobileNumber() != null) {
persistenceCustomer.setMobileNumber(customer.getMobileNumber());
}
if (customer.getInvoices().size() > 0) {
persistenceCustomer.setInvoices(customer.getInvoices());
}
return cutomerRepository.save(persistenceCustomer);
}
The api in postman :
and there's data in database:
As far as I can see in your code, everything looks ok up to the point of where you ask for the size of the invoices collection in the updateCustomer method of the service:
if (customer.getInvoices().size() > 0) {
persistenceCustomer.setInvoices(customer.getInvoices());
}
In your Postman screenshot there are no invoices being passed, so, even if the CustomerDTO has a property (which I assume it does) to hold a List (or any suitable collection) for the invoices, if no invoices information is being received in the body the request, that field will be likely be null, which will trigger the exception you are catching once you try to return the size of the (null) invoices property. So I am betting on that you are getting a nullPointerException instead of a real NOT_FOUND.
So, one possible solution would be to check whether the invoices property of customer is null before attempting to get the collection size.
Hope this helps.
I'm creating an endpoint using Spring Boot which executes a combination of system commands (java.lang.Runtime API) to generate a zip file to return to the client upon request, here's the code.
#GetMapping(value = "generateZipFile")
public ResponseEntity<Resource> generateZipFile(#RequestParam("id") Integer id) throws IOException {
org.springframework.core.io.Resource resource = null;
//generate zip file using commandline
resource = service.generateTmpResource(id);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, "application/zip")
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"randomFile.zip\"")
.body(resource);
//somehow delete generated file here after client receives it
}
I cannot keep stacking up the files on the server for obvious disk limit reasons, so I'm looking for a way to delete the files as soon as the client downloads them. Is there a solution in Spring Boot for this? I basically need to hook a callback that would do the cleanup after the user receives the resource.
I'm using Spring Boot 2.0.6
You can create a new thread but a best solution would be create a ThreadPoolExecutor in order to manage threads or also Scheduled annotation helps us.
new Thread() {
#Override
public void run() {
service.cleanup(id);
}
}.start();
UPDATED
A best answer, it would be using a Stack combine with Thread.
Here is the solution that I've done.
https://github.com/jjohxx/example-thread
I ended up using a HandlerInterceptorAdapter, afterCompletion was the callback I needed. The only challenge I had to deal with was passing through the id of the resource to cleanup, which I handled by adding a header in my controller method:
#GetMapping(value = "generateZipFile")
public ResponseEntity<Resource> genereateZipFile(#RequestParam("id") Integer id,
RedirectAttributes redirectAttributes) throws IOException {
org.springframework.core.io.Resource resource = myService.generateTmpResource(id);;
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, "application/zip")
.header(MyInterceptor.TMP_RESOURCE_ID_HEADER, id.toString())
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"someFile.zip\"")
.body(resource);
}
The interceptor code:
#Component
public class MyInterceptor extends HandlerInterceptorAdapter {
public static final String TMP_RESOURCE_ID_HEADER = "Tmp-ID";
#Autowired
private MyService myService;
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
if(response == null || !response.containsHeader(TMP_RESOURCE_ID_HEADER)) return;
String tmpFileId = response.getHeader(TMP_RESOURCE_ID_HEADER);
myService.cleanup(tmpFileId);
}
}
For more information about interceptors see here.
I'm developing Rest API with Spring Boot 2 and I'm trying to create an ExceptionHandler but it seems it doesn't working.
I have the following #GetMapping method:
#GetMapping("/customers/{customerId}")
public Customer getCustomerById(#PathVariable Long customerId) {
log.debug("### Enter: getCustomerById() for id: " + customerId);
Optional<Customer> customer = customerRepository.findById(customerId);
if (!customer.isPresent()) {
throw new CustomerNotFoundException("The customer with id: " + customerId + " was not found!");
}
return customer.get();
}
The customerRepository it's an interface that extends CrudRepository interface.
The #ExceptionHandler for customerNotFoundException it's the following:
#ExceptionHandler(CustomerNotFoundException.class)
public final ResponseEntity handleCustomerNotFoundException
(CustomerNotFoundException customerNotFoundException, WebRequest webRequest) {
log.error("### Oups! We have a CustomerNotFoundException!!!");
ExceptionResponse exceptionResponse = new ExceptionResponse(
new Date(),
customerNotFoundException.getMessage(),
webRequest.getDescription(false));
return new ResponseEntity(customerNotFoundException, HttpStatus.NOT_FOUND);
}
I also have annotated the ResponseEntityExceptionHandler extended class as following:
#Slf4j
#RestController
#ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
The problem is that when I call a request for a customerId that it's not exists in the database, I don't receive only the CustomerNotFoundException message, but a very long stacktrace like:
{"cause":null,"stackTrace":[{"methodName":"getCustomerById","fileName":"CustomerResource.java","lineNumber":37,"className":"org.pdm.ib.pmt.router.controls.CustomerResource","nativeMethod":false},{"methodName":"invoke0","fileName":"NativeMethodAccessorImpl.java","lineNumber":-2,"className":"sun.reflect.NativeMethodAccessorImpl","nativeMethod":true},{"methodName":"invoke","fileName":"NativeMethodAccessorImpl.java","lineNumber":62,"className":"sun.reflect.NativeMethodAccessorImpl","nativeMethod":false},{"methodName":"invoke","fileName":"DelegatingMethodAccessorImpl.java","lineNumber":43,"className":"sun.reflect.DelegatingMethodAccessorImpl","nativeMethod":false},{"methodName":"invoke","fileName":"Method.java","lineNumber":498,"className":"java.lang.reflect.Method","nativeMethod":false},
and so on...
what is the problem?
Thank you!
Thanks to #bestwishes, I've found the solution. This was returning
new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND)
instead returning
new ResponseEntity(customerNotFoundException, HttpStatus.NOT_FOUND)
in handleCustomerNotFoundException method from CustomResponseEntityExceptionHandler class.
I'm developing REST service which, in turn, will query slow legacy system so response time will be measured in seconds. We also expect massive load so I was thinking about asynchronous/non-blocking approaches to avoid hundreds of "servlet" threads blocked on calls to slow system.
As I see this can be implemented using AsyncContext which is present in new servlet API specs. I even developed small prototype and it seems to be working.
On the other hand it looks like I can achieve the same using Spring WebFlux.
Unfortunately I did not find any example where custom "backend" calls are wrapped with Mono/Flux. Most of the examples just reuse already-prepared reactive connectors, like ReactiveCassandraOperations.java, etc.
My data flow is the following:
JS client --> Spring RestController --> send request to Kafka topic --> read response from Kafka reply topic --> return data to client
Can I wrap Kafka steps into Mono/Flux and how to do this?
How my RestController method should look like?
Here is my simple implementation which achieves the same using Servlet 3.1 API
//took the idea from some Jetty examples
public class AsyncRestServlet extends HttpServlet {
...
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String result = (String) req.getAttribute(RESULTS_ATTR);
if (result == null) { //data not ready yet: schedule async processing
final AsyncContext async = req.startAsync();
//generate some unique request ID
String uid = "req-" + String.valueOf(req.hashCode());
//share it to Kafka receive together with AsyncContext
//when Kafka receiver will get the response it will put it in Servlet request attribute and call async.dispatch()
//This doGet() method will be called again and it will send the response to client
receiver.rememberKey(uid, async);
//send request to Kafka
sender.send(uid, param);
//data is not ready yet so we are releasing Servlet thread
return;
}
//return result as html response
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println(result);
out.close();
}
Here's a short example - Not the WebFlux client you probably had in mind, but at least it would enable you to utilize Flux and Mono for asynchronous processing, which I interpreted to be the point of your question. The web objects should work without additional configurations, but of course you will need to configure Kafka as the KafkaTemplate object will not work on its own.
#Bean // Using org.springframework.web.reactive.function.server.RouterFunction<ServerResponse>
public RouterFunction<ServerResponse> sendMessageToTopic(KafkaController kafkaController){
return RouterFunctions.route(RequestPredicates.POST("/endpoint"), kafkaController::sendMessage);
}
#Component
public class ResponseHandler {
public getServerResponse() {
return ServerResponse.ok().body(Mono.just(Status.SUCCESS), String.class);
}
}
#Component
public class KafkaController {
public Mono<ServerResponse> auditInvalidTransaction(ServerRequest request) {
return request.bodyToMono(TopicMsgMap.class)
// your HTTP call may not return immediately without this
.subscribeOn(Schedulers.single()) // for a single worker thread
.flatMap(topicMsgMap -> {
MyKafkaPublisher.sendMessages(topicMsgMap);
}.flatMap(responseHandler::getServerResponse);
}
}
#Data // model class just to easily convert the ServerRequest (from json, for ex.)
// + ~#constructors
public class TopicMsgMap() {
private Map<String, String> topicMsgMap;
}
#Service // Using org.springframework.kafka.core.KafkaTemplate<String, String>
public class MyKafkaPublisher {
#Autowired
private KafkaTemplate<String, String> template;
#Value("${topic1}")
private String topic1;
#Value("${topic2}")
private String topic2;
public void sendMessages(Map<String, String> topicMsgMap){
topicMsgMap.forEach((top, msg) -> {
if (topic.equals("topic1") kafkaTemplate.send(topic1, message);
if (topic.equals("topic2") kafkaTemplate.send(topic2, message);
});
}
}
Guessing this isn't the use-case you had in mind, but hope you find this general structure useful.
There is several approaches including KafkaReplyingRestTemplate for this problem but continuing your approach in servlet api's the solution will be something like this in spring Webflux.
Your Controller method looks like this:
#RequestMapping(path = "/completable-future", method = RequestMethod.POST)
Mono<Response> asyncTransaction(#RequestBody RequestDto requestDto, #RequestHeader Map<String, String> requestHeaders) {
String internalTransactionId = UUID.randomUUID().toString();
kafkaSender.send(Request.builder()
.transactionId(requestHeaders.get("transactionId"))
.internalTransactionId(internalTransactionId)
.sourceIban(requestDto.getSourceIban())
.destIban(requestDto.getDestIban())
.build());
CompletableFuture<Response> completableFuture = new CompletableFuture();
taskHolder.pushTask(completableFuture, internalTransactionId);
return Mono.fromFuture(completableFuture);
}
Your taskHolder component will be something like this:
#Component
public class TaskHolder {
private Map<String, CompletableFuture> taskHolder = new ConcurrentHashMap();
public void pushTask(CompletableFuture<Response> task, String transactionId) {
this.taskHolder.put(transactionId, task);
}
public Optional<CompletableFuture> remove(String transactionId) {
return Optional.ofNullable(this.taskHolder.remove(transactionId));
}
}
And finally your Kafka ResponseListener looks like this:
#Component
public class ResponseListener {
#Autowired
TaskHolder taskHolder;
#KafkaListener(topics = "reactive-response-topic", groupId = "test")
public void listen(Response response) {
taskHolder.remove(response.getInternalTransactionId()).orElse(
new CompletableFuture()).complete(response);
}
}
In this example I used internalTransactionId as CorrelationId but you can use "kafka_correlationId" that is a known kafka header.
I use spring-boot as a backend server. It has tens of Action Methods. As usual Some of them contains validation. Actually I use BindingResult and returns validation error for returning Http 400 Status.
#CrossOrigin
#RestController
public class ValidationTestController {
#RequestMapping(value = {"/validation-test", "/validation-test/"}, method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<String> login(#RequestBody #Valid final TestData data, final BindingResult result) {
if (result.hasErrors()) {
return new ResponseEntity<>("Sorry incoming data is not valid!", HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("OK!", HttpStatus.OK);
}
private static final class TestData {
#NotNull
private String value;
}
}
My aim is removing follpwing lines:
if (result.hasErrors()) {
return new ResponseEntity<>("Sorry incoming data is not valid!", HttpStatus.BAD_REQUEST);
}
IMHO it's a cross cutting concern like Authentication and Auditing. I want to handle it in a one global ErrorHandler Method. It's possible to throw a CustomValidationException Before executing the method. So I can handle the exception in ErrorController.
Yes, you can centralize the exception handling logic at one place, using #ExceptionHandler which is a ControllerAdvice from Spring.
You can look at here