package at.gv.egiz.moazs.service; import at.gv.egiz.moazs.backend.MsgResponseSinkHub; import at.gv.egiz.moazs.preprocess.DeliveryRequestAugmenter; import at.gv.egiz.moazs.preprocess.MzsDeliveryRequestValidator; import at.gv.egiz.moazs.repository.DeliveryRepository; import at.gv.egiz.moazs.scheme.Msg2MzsConverter; import at.gv.egiz.moazs.scheme.RequestStatusResponse; import at.gv.zustellung.app2mzs.xsd.App2MzsPortType; import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType; import at.gv.zustellung.app2mzs.xsd.DeliveryResponseType; import org.apache.cxf.annotations.SchemaValidation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.jws.WebParam; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import static at.gv.egiz.moazs.MoaZSException.moaZSException; import static at.gv.zustellung.app2mzs.xsd.PartialSuccessType.partialSuccessTypeBuilder; import static java.lang.String.format; import static java.util.concurrent.CompletableFuture.supplyAsync; @Service @SchemaValidation(type = SchemaValidation.SchemaValidationType.BOTH) @org.apache.cxf.feature.Features (features = "org.apache.cxf.ext.logging.LoggingFeature") public class MzsService implements App2MzsPortType { private static final Logger log = LoggerFactory.getLogger(MzsService.class); private static final String MZS_SERVICE_ERROR_MSG = "An error occurred while processing DeliveryRequest " + "with AppDeliveryID=%s."; private static final String RESPONSE_MISSING_ERROR_MSG = "Could not get a response for AppDeliveryID=%s."; private static final String SERVICE_TIME_OUT_REACHED_MSG = "Backend processing for DeliveryRequest with " + "AppDeliveryID=%s timed out. "; private final DeliveryRepository repository; private final Consumer backend; private final DeliveryRequestAugmenter augmenter; private final Msg2MzsConverter converter; private final MsgResponseSinkHub hub; private final MzsDeliveryRequestValidator validator; @Autowired public MzsService(DeliveryRepository repository, Consumer deliveryRequestBackend, DeliveryRequestAugmenter augmenter, Msg2MzsConverter converter, MsgResponseSinkHub hub, MzsDeliveryRequestValidator validator) { this.repository = repository; this.backend = deliveryRequestBackend; this.augmenter = augmenter; this.converter = converter; this.hub = hub; this.validator = validator; } @Override public DeliveryResponseType app2Mzs( @WebParam(partName = "DeliveryRequest",name = "DeliveryRequest") DeliveryRequestType deliveryRequest) { var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID(); var completeRequest = augmenter.augment(deliveryRequest); validator.isRequestValid(completeRequest); var requestProcessed = supplyAsync(() -> process(completeRequest)); try { var serviceTimeout = completeRequest.getConfig().getServiceTimeout(); RequestStatusResponse response; if (serviceTimeout == null) { response = requestProcessed.get(); } else { response = requestProcessed.get(serviceTimeout.longValue(), TimeUnit.SECONDS); } var status = response.getResponse(); var binaryStatus = repository.retrieveBinaryResponse(response.getResponseID()); return converter.convert(status, binaryStatus); } catch (TimeoutException e) { return applySinkHubToAsyncResponse(appDeliveryID, completeRequest, requestProcessed); } catch (Exception e) { var message = format(MZS_SERVICE_ERROR_MSG, appDeliveryID); throw moaZSException(message, e); } } private RequestStatusResponse process(DeliveryRequestType deliveryRequest) { var appDeliveryID = deliveryRequest.getMetaData().getAppDeliveryID(); repository.store(deliveryRequest); backend.accept(appDeliveryID); var responseID = RequestStatusResponse.getResponseID(appDeliveryID); return (RequestStatusResponse) repository.retrieveResponse(responseID) .orElseThrow(() -> moaZSException(format(RESPONSE_MISSING_ERROR_MSG, appDeliveryID))); } private DeliveryResponseType applySinkHubToAsyncResponse(String appDeliveryID, DeliveryRequestType request, CompletableFuture requestProcessed) { log.info(format(SERVICE_TIME_OUT_REACHED_MSG, appDeliveryID)); var sinkParams = request.getConfig().getMsgResponseSinks(); requestProcessed.thenAcceptAsync(response -> hub.applySinks(response, sinkParams)); return generatePartialSuccessResponse(appDeliveryID); } private DeliveryResponseType generatePartialSuccessResponse(String appDeliveryId) { var partial = partialSuccessTypeBuilder() .withAppDeliveryID(appDeliveryId) .build(); return DeliveryResponseType.deliveryResponseTypeBuilder() .withPartialSuccess(partial) .build(); } }