rollback transaction in microservice(grpc) - spring

I have two microservices called grpcLogin and grpcCityPlace.
Which are related to grps and each other having separate datasource.
From microservis grpcLogin I call microservis grpcCityPlace.
I want the Microservis grpcCityPlace operation to be rolled back if an error occurs in Microservice grpcLogin .
How can I perform the rollback operation on the microservice grpcCityPlace by the microservice grpcLogin with the spring boot and grpc?
I also want rollback in delete and update operations.
grpcLogin:
public AdminUserModel create(#NotNull AdminUserModel adminUserModel) throws UtilityException, IllegalAccessException, BusinessException, InvocationTargetException {
//create Admin
AdminUser adminUser = new AdminUser();
adminUser.setFirstName(adminUserModel.getFirstName());
adminUser.setLastName(adminUserModel.getLastName());
adminUser.setPassword(PasswordEncoderGenerator.generate(adminUserModel.getPassword()));
adminUser.setUsername(adminUserModel.getUsername());
adminUser.setDateOfBirth(CalendarTools.getDateFromCustomDate(adminUserModel.getDateOfBirth()));
adminUser.setGender(etcItemService.findByIdAndCheckEntity(adminUserModel.getGender_id(), GenderEnum.class,null,true));
adminUser = adminUserRepository.save(adminUser);
//add cityPlace For Admin
CreateRequestModel createRequestModel = CreateRequestModel.newBuilder()
.setAdminUserId(adminUser.getId())
.setCityId(cityReadOneResponse.getId())
.setLatitude("35.791354")
.setLongitude("51.356406")
.setTitle("OfficePlace")
.build();
//call grpcCreate
final CreateResponseModel createResponseModel= this.cityPlaceStub.grpcCreate(createRequestModel);
//It's just written to express what I mean.
if(true){
throw new Exception();
}
adminUserModel.setId(adminUser.getId());
return adminUserModel;
}
grpcCityPlace:
#Override
public void grpcCreate(CreateRequestModel createRequestModel, StreamObserver<CreateResponseModel> responseObserver) {
CreateResponseModel.Builder createResponseModel = CreateResponseModel.newBuilder();
CityPlaceModel cityPlaceModel = new CityPlaceModel();
cityPlaceModel.setCity_id(createRequestModel.getCityId());
cityPlaceModel.setLatitude(createRequestModel.getLatitude());
cityPlaceModel.setLongitude(createRequestModel.getLongitude());
cityPlaceModel.setAdminUserId(createRequestModel.getAdminUserId());
cityPlaceModel.setTitle(createRequestModel.getTitle());
CityPlace cityPlace = new CityPlace();
cityPlace.setAdminUserId(cityPlaceModel.getAdminUserId());
City city = cityRepository.findById(cityPlaceModel.getCity_id()).get();
cityPlace.setCity(city);
cityPlace.setLatitude(cityPlaceModel.getLatitude());
cityPlace.setLongitude(cityPlaceModel.getLongitude());
cityPlace.setTitle(cityPlaceModel.getTitle());
cityPlace = cityPlaceRepository.save(cityPlace);
cityPlaceModel = this.create(cityPlaceModel);
responseObserver.onNext(createResponseModel.build());
responseObserver.onCompleted();
}

you can use saga pattern to with compensating transaction which will rollback previous transaction.
Or you can have 2 phase commit algorithm implemented with Transaction Coordinator and Transaction managers.

Related

Dynamics 365 Plugin An error occurred in FollowUpPlugin 0x80040265

I have a plugin that is executed when a new instance from the sales order detail entity is created. when creating the instance an error appears in the plugin trace log:
get Ref
productIdRef
de_prdrate: 19
de_salesprdrate: 19
System.ServiceModel.FaultException`1[Microsoft.Xrm.Sdk.OrganizationServiceFault]: The product and the unit cannot be updated while the entity is locked. (Fault Detail is equal to Exception details:
ErrorCode: 0x80040265
Message: The product and the unit cannot be updated while the entity is locked.
OriginalException: PluginExecution ExceptionSource: PluginExecution
Note:
there is also a System workflow running on the sales order detail entity on the create event.
the plugin works fine on the quote details entity without any exceptions or errors.
I think the problem is that my plugin and the system workflow work on the same event. I did not find any solution to this problem. I appreciate any suggestions.
The code is below:
public class Update_Salesdetails_Field : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
if (entity.LogicalName != "salesorderdetail")
{
tracingService.Trace("Target entity is not Sales Order Detail! plug-in was not registered correctly! Exit PlugIn", serviceProvider);
throw new InvalidPluginExecutionException("The current entity is not the Sales Order Detail entity");
}
tracingService.Trace("get Ref", serviceProvider);
var productIdRef = entity.GetAttributeValue<EntityReference>("productid");
tracingService.Trace("productIdRef", serviceProvider);
var productEntity = service.Retrieve("product", productIdRef.Id, new ColumnSet("de_prdrate"));
tracingService.Trace("de_prdrate: " + productEntity["de_prdrate"]);
entity["de_salesprdrate"] = productEntity["de_prdrate"];
tracingService.Trace("de_salesprdrate " + entity["de_salesprdrate"]);
service.Update(entity);
tracingService.Trace("Update salesprdrate " + entity["de_salesprdrate"]);
}
catch (FaultException<OrganizationServiceFault> ex)
{
tracingService.Trace("exception:", ex.ToString());
tracingService.Trace(ex.StackTrace);
if (ex.InnerException != null)
{
tracingService.Trace("inner exception:", ex.InnerException.ToString());
tracingService.Trace(ex.InnerException.StackTrace);
}
throw new InvalidPluginExecutionException(ex.Message);
}
catch (Exception ex)
{
tracingService.Trace("FollowUpPlugin: {0}", ex.ToString());
tracingService.Trace(ex.StackTrace);
throw;
}
}
}
}
In the post operation stage of the plugin pipeline for message "Update" the system blocks modifications to the entity object that is subject of the plugin pipeline. (This is the entity that was retrieved from tne context.InputParameters collection.)
Instead the plugin should be registered on the pre validation or pre operation stage. In these plugin pipeline stages attributes can be modified on entity found in context.InputParameters["Target"].
The entity object must not be updated explicitly, so remove this line:
service.Update(entity);

#Cacheable is not able to update

In spring framework we have #Cacheable to cache data right. Now my requirement is i want to retrieve all data form database by using Get method.
Controller
#RequestMapping(value = "/getUploadData", method = RequestMethod.GET)
public ResponseEntity<List<Ticket>> getUploadFileData() throws IOException {
return new ResponseEntity<>(ticketBookingService.getFileUploadData(), HttpStatus.OK);
}
Service
#Cacheable(value="ticketsCache")
public List<Ticket> getFileUploadData() {
List<Ticket> listOfData = (List<Ticket>) ticketBookingDao.findAll();
return listOfData;
}
}
output:
click image here to check output
http://localhost:8080/api/tickets/getUploadData
[{"ticketId":1,"passengerName":"Sean","bookingDate":1502649000000,"sourceStation":"Pune","destStation":"Mumbai","email":"sean.s2017#yahoo.com"},{"ticketId":2,"passengerName":"Raj","bookingDate":1502476200000,"sourceStation":"Chennai","destStation":"Mumbai","email":"raj.s2007#siffy.com"},{"ticketId":3,"passengerName":"Martin","bookingDate":1502735400000,"sourceStation":"Delhi","destStation":"Mumbai","email":"martin.s2001#xyz.com"},{"ticketId":4,"passengerName":"John","bookingDate":1503253800000,"sourceStation":"Chennai","destStation":"Mumbai","email":"john.s2011#yahoo.com"}]
Now i will do get and put operation by ticketid.
Get:
Controller:
#GetMapping(value="/ticket/{ticketId}")
public Ticket getTicketById(#PathVariable("ticketId")Integer ticketId){
return ticketBookingService.getTicketById(ticketId);
}
Service:
#Cacheable(value="ticketsCache",key="#ticketId",unless="#result==null")
public Ticket getTicketById(Integer ticketId) {
return ticketBookingDao.findOne(ticketId);
}
http://localhost:8080/api/tickets/ticket/1
{"ticketId":1,"passengerName":"Sean","bookingDate":1502649000000,"sourceStation":"Pune","destStation":"Mumbai","email":"sean.s2017#yahoo.com"}
Now when i do update email by using ticketid:
Put: controller
#PutMapping(value="/ticket/{ticketId}/{newEmail:.+}")
public Ticket updateTicket(#PathVariable("ticketId")Integer ticketId,#PathVariable("newEmail")String newEmail){
return ticketBookingService.updateTicket(ticketId,newEmail);
}
Service:
#CachePut(value="ticketsCache",key="#ticketId")
public Ticket updateTicket(Integer ticketId, String newEmail) {
Ticket upadedTicket = null;
Ticket ticketFromDb = ticketBookingDao.findOne(ticketId);
if(ticketFromDb != null){
ticketFromDb.setEmail(newEmail);
upadedTicket = ticketBookingDao.save(ticketFromDb);
}
return upadedTicket;
}
http://localhost:8080/api/tickets/ticket/1/abcd#yahoo.com
{
"ticketId": 1,
"passengerName": "Sean",
"bookingDate": 1502649000000,
"sourceStation": "Pune",
"destStation": "Mumbai",
"email": "abcd#yahoo.com"
}
Now when get data by using ID changes are updating.
http://localhost:8080/api/tickets/ticket/1
{"ticketId":1,"passengerName":"Sean","bookingDate":1502649000000,"sourceStation":"Pune","destStation":"Mumbai","email":"abcd#yahoo.com"}
Now my Question is if i try to get all data by using above first URL my changes are not reflecting.
http://localhost:8080/api/tickets/getUploadData
[{"ticketId":1,"passengerName":"Sean","bookingDate":1502649000000,"sourceStation":"Pune","destStation":"Mumbai","email":"sean.s2017#yahoo.com"},{"ticketId":2,"passengerName":"Raj","bookingDate":1502476200000,"sourceStation":"Chennai","destStation":"Mumbai","email":"raj.s2007#siffy.com"},{"ticketId":3,"passengerName":"Martin","bookingDate":1502735400000,"sourceStation":"Delhi","destStation":"Mumbai","email":"martin.s2001#xyz.com"},{"ticketId":4,"passengerName":"John","bookingDate":1503253800000,"sourceStation":"Chennai","destStation":"Mumbai","email":"john.s2011#yahoo.com"}]
Suggest me how to reslove this issue
You cannot bulk update the cache with Spring.
Please check the following issue - closed with status declined:
Thanks for creating the issue but I am not keen to add this extra complexity to the cache abstraction. It is not meant to manage state for you (the next logical step if we allow this is that we have to keep the returned list in sync with each item). And if we don't we are inconsistent and we merely provide a way to talk to the cache using annotations. That's not very helpful.
Back to your example, this is typically what a second level cache is meant to do for you. This is not in the scope of the cache abstraction.

notification received but data is not updated to iphone

i'm using restful web service. In that i have a method, by using this method i send notification and mails. My method is working fine, only problem is that notification pops up 2,3 seconds before you get success response after calling this method.
#Override
#Transactional
public MedikmResponse createNPostCcbrQuestion(CreateNPostCcbrQuestionRequest createNPostCcbrQuestionRequest, HttpServletRequest request) {
logger.info("#########################Post Ccbr Question#############################");
MedikmResponse medikmResponse=new MedikmResponse();
try {
String token = request.getHeader("authToken");
User user = userDao.findUserByAuthToken(token);
if (user != null) {
/**
* Question Creation
*/
CcbrQuestion ccbrQuestion=new CcbrQuestion();
ccbrQuestion.setQuestiomtext(createNPostCcbrQuestionRequest.getCcbrQuestionText());
Integer questionId=ccbrQuestionDao.saveCcbrQuestion(ccbrQuestion);
ccbrQuestion.setQuestionId(questionId);
/* ***
* Question post
*/
TbDiscussionForumQuestion tbDiscussionForumQuestion=new TbDiscussionForumQuestion();
tbDiscussionForumQuestion.setCCBRQuestionId(ccbrQuestion);
tbDiscussionForumQuestion.setTBDiscussionId(new TbDiscussionForum(createNPostCcbrQuestionRequest.getTbdiscussionId()));
tbDiscussionForumQuestion.setPhysicianId(new Physician(user.getPhysicianId().getPhysicianId()));
tbDiscussionForumQuestion.setQuestion(createNPostCcbrQuestionRequest.getCcbrQuestionText());
tbDiscussionForumQuestion.setQuestionDate(new Date());
//tbDiscussionForumQuestionDao.saveTbDiscussionForumQuestion(tbDiscussionForumQuestion);
TbDiscussionForum discussionForum=tbDiscussionForumDao.findTbDiscussionForumbyTbId(createNPostCcbrQuestionRequest.getTbdiscussionId());
discussionForum.getTbDiscussionForumQuestionCollection().add(tbDiscussionForumQuestion);
tbDiscussionForumDao.updateTbDiscussionForum(discussionForum);
String[] deviceIdList=getParticpentDeviceIdList(discussionForum.getCaseId().getCaseId(), user);
medikmResponse.setAuthenticationKey(user.getAuthToken());
notificationSender.sendPostedCcbrQuestionToVMDCParticipant(discussionForum.getCaseId(), user.getPhysicianId(), createNPostCcbrQuestionRequest.getCcbrQuestionText());//sending mail
if(deviceIdList.length!=0){
notificationService.sendNotificationToIOS(deviceIdList,discussionForum.getCaseId().getCaseId(),createNPostCcbrQuestionRequest.getTbdiscussionId(),"Question");
}
medikmResponse.setResponseCode(MedikmConstants.SUCCESS_CODE);
medikmResponse.setResponseMessage(MedikmConstants.SUCCESS_MESSAGE);
}else{
medikmResponse.setResponseCode(MedikmConstants.FAILURE_CODE);
medikmResponse.setResponseMessage(MedikmConstants.USER_DOES_NOT_EXIST);
logger.info("############ USER_DOES_NOT_EXIST #############"+ MedikmConstants.USER_DOES_NOT_EXIST);
}
return medikmResponse;
}catch(Exception ex){
ex.printStackTrace();
return null;
}
}
for this i add #Async annotation in the sendNotificationToIOS method and it works for me after adding this annotation need to add in your xml

Using JAX-RS and trying to DELETE an item

I am currently working in Enterprise Java and I'm a newbie. I am trying to create a method which should delete a selected item from a data table. My project contains Graphical User Interface elements from "http://www.primefaces.org/showcase/".
The deletion is made through a web-service.
This is the method I created so far:
public boolean delete(String articleId) {
Client client = ClientBuilder.newClient();
WebTarget target
= client.target(DELETE_URL);//this is a String
//TODO call ws method delete
try{
target.request()....;
} catch(Exception ex) {
LOGGER.error("Delete Article Error ", ex);
}
return true;
}
Could you tell me how can I handle the deletion in an appropiate way?
All the best!
In your case the following should do the trick.
target.request().delete(Response.class)

Programatically bridge a QueueChannel to a MessageChannel in Spring

I'm attempting to wire a queue to the front of a MessageChannel, and I need to do so programatically so it can be done at run time in response to an osgi:listener being triggered. So far I've got:
public void addService(MessageChannel mc, Map<String,Object> properties)
{
//Create the queue and the QueueChannel
BlockingQueue<Message<?>> q = new LinkedBlockingQueue<Message<?>>();
QueueChannel qc = new QueueChannel(q);
//Create the Bridge and set the output to the input parameter channel
BridgeHandler b = new BridgeHandler();
b.setOutputChannel(mc);
//Presumably, I need something here to poll the QueueChannel
//and drop it onto the bridge. This is where I get lost
}
Looking through the various relevant classes, I came up with:
PollerMetadata pm = new PollerMetadata();
pm.setTrigger(new IntervalTrigger(10));
PollingConsumer pc = new PollingConsumer(qc, b);
but I'm not able to put it all together. What am I missing?
So, the solution that ended up working for me was:
public void addEngineService(MessageChannel mc, Map<String,Object> properties)
{
//Create the queue and the QueueChannel
BlockingQueue<Message<?>> q = new LinkedBlockingQueue<Message<?>>();
QueueChannel qc = new QueueChannel(q);
//Create the Bridge and set the output to the input parameter channel
BridgeHandler b = new BridgeHandler();
b.setOutputChannel(mc);
//Setup a Polling Consumer to poll the queue channel and
//retrieve 1 thing at a time
PollingConsumer pc = new PollingConsumer(qc, b);
pc.setMaxMessagesPerPoll(1);
//Now use an interval trigger to poll every 10 ms and attach it
IntervalTrigger trig = new IntervalTrigger(10, TimeUnit.MILLISECONDS);
trig.setInitialDelay(0);
trig.setFixedRate(true);
pc.setTrigger(trig);
//Now set a task scheduler and start it
pc.setTaskScheduler(taskSched);
pc.setAutoStartup(true);
pc.start();
}
I'm not terribly clear if all the above is explicitly needed, but neither the trigger or the task scheduler alone worked, I did appear to need both. I should also note the taskSched used was the default taskScheduler dependency injected from spring via
<property name="taskSched" ref="taskScheduler"/>

Resources