/** * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mifosplatform.batch.service; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import org.mifosplatform.batch.command.CommandContext; import org.mifosplatform.batch.command.CommandStrategy; import org.mifosplatform.batch.command.CommandStrategyProvider; import org.mifosplatform.batch.domain.BatchRequest; import org.mifosplatform.batch.domain.BatchResponse; import org.mifosplatform.batch.exception.ErrorHandler; import org.mifosplatform.batch.exception.ErrorInfo; import org.mifosplatform.batch.service.ResolutionHelper.BatchRequestNode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import com.google.gson.Gson; import edu.emory.mathcs.backport.java.util.Collections; /** * Implementation for {@link BatchApiService} to iterate through all the * incoming requests and obtain the appropriate CommandStrategy from * CommandStrategyProvider. * * @author Rishabh Shukla * * @see org.mifosplatform.batch.domain.BatchRequest * @see org.mifosplatform.batch.domain.BatchResponse * @see org.mifosplatform.batch.command.CommandStrategyProvider */ @Service public class BatchApiServiceImpl implements BatchApiService { private final CommandStrategyProvider strategyProvider; private final ResolutionHelper resolutionHelper; private final TransactionTemplate transactionTemplate; private List<BatchResponse> checkList = new ArrayList<>(); /** * Constructs a 'BatchApiServiceImpl' with an argument of * {@link org.mifosplatform.batch.command.CommandStrategyProvider} type. * * @param strategyProvider * @param resolutionHelper * @param transactionTemplate */ @Autowired public BatchApiServiceImpl(final CommandStrategyProvider strategyProvider, final ResolutionHelper resolutionHelper, final TransactionTemplate transactionTemplate) { this.strategyProvider = strategyProvider; this.resolutionHelper = resolutionHelper; this.transactionTemplate = transactionTemplate; } /** * Returns the response list by getting a proper * {@link org.mifosplatform.batch.command.CommandStrategy}. execute() method * of acquired commandStrategy is then provided with the separate Request. * * @param requestList * @param uriInfo * @return List<BatchResponse> */ private List<BatchResponse> handleBatchRequests(final List<BatchRequest> requestList, final UriInfo uriInfo) { final List<BatchResponse> responseList = new ArrayList<>(requestList.size()); final List<BatchRequestNode> batchRequestNodes = this.resolutionHelper.getDependingRequests(requestList); checkList.clear(); for (BatchRequestNode rootNode : batchRequestNodes) { final BatchRequest rootRequest = rootNode.getRequest(); final CommandStrategy commandStrategy = this.strategyProvider.getCommandStrategy(CommandContext .resource(rootRequest.getRelativeUrl()).method(rootRequest.getMethod()).build()); final BatchResponse rootResponse = commandStrategy.execute(rootRequest, uriInfo); responseList.add(rootResponse); responseList.addAll(this.processChildRequests(rootNode, rootResponse, uriInfo)); } Collections.sort(responseList, new Comparator<BatchResponse>() { @Override public int compare(BatchResponse source, BatchResponse testee) { return source.getRequestId().compareTo(testee.getRequestId()); } }); checkList = responseList; return responseList; } private List<BatchResponse> processChildRequests(final BatchRequestNode rootRequest, BatchResponse rootResponse, UriInfo uriInfo) { final List<BatchResponse> childResponses = new ArrayList<>(); if (rootRequest.getChildRequests().size() > 0) { for (BatchRequestNode childNode : rootRequest.getChildRequests()) { BatchRequest childRequest = childNode.getRequest(); BatchResponse childResponse; try { if (rootResponse.getStatusCode().equals(200)) { childRequest = this.resolutionHelper.resoluteRequest(childRequest, rootResponse); final CommandStrategy commandStrategy = this.strategyProvider.getCommandStrategy(CommandContext .resource(childRequest.getRelativeUrl()).method(childRequest.getMethod()).build()); childResponse = commandStrategy.execute(childRequest, uriInfo); } else { // Something went wrong with the parent request, create // a response with status code 409 childResponse = new BatchResponse(); childResponse.setRequestId(childRequest.getRequestId()); childResponse.setStatusCode(Status.CONFLICT.getStatusCode()); // Some detail information about the error final ErrorInfo conflictError = new ErrorInfo(Status.CONFLICT.getStatusCode(), 8001, "Parent request with id " + rootResponse.getRequestId() + " was erroneous!"); childResponse.setBody(conflictError.getMessage()); } childResponses.addAll(this.processChildRequests(childNode, childResponse, uriInfo)); } catch (Throwable ex) { childResponse = new BatchResponse(); childResponse.setRequestId(childRequest.getRequestId()); childResponse.setStatusCode(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); childResponse.setBody(ex.getMessage()); } childResponses.add(childResponse); } } return childResponses; } @Override public List<BatchResponse> handleBatchRequestsWithoutEnclosingTransaction(final List<BatchRequest> requestList, UriInfo uriInfo) { return handleBatchRequests(requestList, uriInfo); } @Override public List<BatchResponse> handleBatchRequestsWithEnclosingTransaction(final List<BatchRequest> requestList, final UriInfo uriInfo) { try { return this.transactionTemplate.execute(new TransactionCallback<List<BatchResponse>>() { @Override public List<BatchResponse> doInTransaction(TransactionStatus status) { try { return handleBatchRequests(requestList, uriInfo); } catch (RuntimeException ex) { ErrorInfo e = ErrorHandler.handler(ex); BatchResponse errResponse = new BatchResponse(); errResponse.setStatusCode(e.getStatusCode()); errResponse.setBody(e.getMessage()); List<BatchResponse> errResponseList = new ArrayList<>(); errResponseList.add(errResponse); status.setRollbackOnly(); return errResponseList; } } }); } catch (TransactionException ex) { ErrorInfo e = ErrorHandler.handler(ex); BatchResponse errResponse = new BatchResponse(); errResponse.setStatusCode(e.getStatusCode()); for (BatchResponse res : checkList) { if (!res.getStatusCode().equals(200)) { errResponse.setBody("Transaction is being rolled back. First erroneous request: \n" + new Gson().toJson(res)); break; } } checkList.clear(); List<BatchResponse> errResponseList = new ArrayList<>(); errResponseList.add(errResponse); return errResponseList; } } }