All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.laz.casual.jca.service.CasualServiceCaller Maven / Gradle / Ivy

There is a newer version: 3.3.0
Show newest version
/*
 * Copyright (c) 2017 - 2024, The casual project. All rights reserved.
 *
 * This software is licensed under the MIT license, https://opensource.org/licenses/MIT
 */

package se.laz.casual.jca.service;

import se.laz.casual.api.CasualServiceApi;
import se.laz.casual.api.buffer.CasualBuffer;
import se.laz.casual.api.buffer.ServiceReturn;
import se.laz.casual.api.buffer.type.ServiceBuffer;
import se.laz.casual.api.flags.AtmiFlags;
import se.laz.casual.api.flags.ErrorState;
import se.laz.casual.api.flags.Flag;
import se.laz.casual.api.flags.ServiceReturnState;
import se.laz.casual.api.network.protocol.messages.CasualNWMessage;
import se.laz.casual.api.network.protocol.messages.exception.CasualProtocolException;
import se.laz.casual.api.service.ServiceDetails;
import se.laz.casual.api.util.PrettyPrinter;
import se.laz.casual.config.ConfigurationService;
import se.laz.casual.config.Domain;
import se.laz.casual.event.Order;
import se.laz.casual.event.ServiceCallEvent;
import se.laz.casual.event.ServiceCallEventPublisher;
import se.laz.casual.event.ServiceCallEventStoreFactory;
import se.laz.casual.jca.CasualManagedConnection;
import se.laz.casual.network.connection.CasualConnectionException;
import se.laz.casual.network.protocol.messages.CasualNWMessageImpl;
import se.laz.casual.network.protocol.messages.domain.CasualDomainDiscoveryReplyMessage;
import se.laz.casual.network.protocol.messages.domain.CasualDomainDiscoveryRequestMessage;
import se.laz.casual.network.protocol.messages.domain.Service;
import se.laz.casual.network.protocol.messages.service.CasualServiceCallReplyMessage;
import se.laz.casual.network.protocol.messages.service.CasualServiceCallRequestMessage;

import javax.transaction.xa.Xid;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Logger;

public class CasualServiceCaller implements CasualServiceApi
{
    private static final Logger LOG = Logger.getLogger(CasualServiceCaller.class.getName());
    private static final String SERVICE_NAME_LITERAL = " serviceName: ";
    private ServiceCallEventPublisher  eventPublisher;
    private final CasualManagedConnection connection;

    private CasualServiceCaller(CasualManagedConnection connection)
    {
        this.connection = connection;
    }

    public static CasualServiceCaller of(CasualManagedConnection connection)
    {
        return new CasualServiceCaller(connection);
    }

    @Override
    public ServiceReturn tpcall(String serviceName, CasualBuffer data, Flag flags)
    {
        try
        {
            throwIfTpCallFlagsInvalid(serviceName, flags);
            return issueAsyncCall(serviceName, data, flags).join().orElseThrow(() -> new CasualConnectionException("result is missing, it should always be returned"));
        }
        catch (Exception e)
        {
            throw new CasualConnectionException(e);
        }
    }

    @Override
    public CompletableFuture>> tpacall(String serviceName, CasualBuffer data, Flag flags)
    {
        throwIfTpacallFlagsInvalid(serviceName, flags);
        return issueAsyncCall(serviceName, data, flags);
    }

    private CompletableFuture>> issueAsyncCall(String serviceName, CasualBuffer data, Flag flags)
    {
        CompletableFuture>> f = new CompletableFuture<>();
        UUID corrId = UUID.randomUUID();
        boolean noReply = flags.isSet(AtmiFlags.TPNOREPLY);
        final UUID execution = UUID.randomUUID();
        final Xid xid = connection.getCurrentXid();

        ServiceCallEvent.Builder eventBuilder = ServiceCallEvent.createBuilder()
                .withTransactionId(xid)
                .withExecution(execution)
                .withParent("")
                .withService(serviceName)
                .withPending(0)
                .withOrder(Order.CONCURRENT)
                .start();

        Optional>> maybeServiceReturnValue = makeServiceCall(corrId, serviceName, data, flags, xid, execution, noReply);
        maybeServiceReturnValue.ifPresent(casualNWMessageCompletableFuture ->
                casualNWMessageCompletableFuture.whenComplete((v, e) -> {
                            if (null != e)
                            {
                                LOG.finest(() -> "service call request failed for corrid: " + PrettyPrinter.casualStringify(corrId) + SERVICE_NAME_LITERAL + serviceName);
                                f.completeExceptionally(e);
                                return;
                            }
                            LOG.finest(() -> "service call request ok for corrid: " + PrettyPrinter.casualStringify(corrId) + SERVICE_NAME_LITERAL + serviceName);
                    eventBuilder.withCode(v.getMessage().getError())
                            .end();
                            getEventPublisher().post(eventBuilder.build());
                    if(!f.isDone())
                    {
                        f.complete(Optional.of(toServiceReturn(v)));
                    }
                }));
        if(noReply)
        {
            eventBuilder.withCode(ErrorState.OK)
                     .end();
            getEventPublisher().post(eventBuilder.build());
            f.complete(Optional.empty());
        }
        return f;
    }

    private void throwIfTpCallFlagsInvalid(String serviceName, Flag flags)
    {
        if(flags.isSet(AtmiFlags.TPNOREPLY))
        {
            throw new CasualProtocolException("tpCall to: + " + serviceName + " with TPNOREPLY - not allowed, should use tpacall");
        }
    }

    private void throwIfTpacallFlagsInvalid(String serviceName, Flag flags)
    {
        if(flags.isSet(AtmiFlags.TPNOREPLY) && !flags.isSet(AtmiFlags.TPNOTRAN))
        {
            throw new CasualProtocolException("tpacall to: " + serviceName + " with TPNOREPLY but missing TPNOTRAN - not allowed");
        }
    }

    @Override
    public boolean serviceExists(String serviceName)
    {
        try
        {
            return serviceExists(UUID.randomUUID(), serviceName);
        }
        catch (Exception e)
        {
            throw new CasualConnectionException(e);
        }
    }

    @Override
    public List serviceDetails(String serviceName)
    {
        List serviceDetailsList = new ArrayList<>();

        CasualNWMessage replyMsg = serviceDiscovery(UUID.randomUUID(), serviceName);
        replyMsg
                .getMessage()
                .getServices()
                .forEach(service -> serviceDetailsList.add(
                        ServiceDetails.createBuilder()
                                .withName(service.getName())
                                .withCategory(service.getCategory())
                                .withTransactionType(service.getTransactionType())
                                .withTimeout(service.getTimeout())
                                .withHops(service.getHops()).build()));

        return serviceDetailsList;
    }

    @Override
    public String toString()
    {
        return "CasualServiceCaller{" +
                "connection=" + connection +
                '}';
    }

    private Optional>> makeServiceCall(UUID corrid, String serviceName, CasualBuffer data, Flag flags, Xid transactionId, UUID execution, boolean noReply)
    {
        Duration timeout = Duration.of(connection.getTransactionTimeout(), ChronoUnit.SECONDS);
        CasualServiceCallRequestMessage serviceRequestMessage = CasualServiceCallRequestMessage.createBuilder()
                .setExecution(execution)
                .setServiceBuffer(ServiceBuffer.of(data))
                .setServiceName(serviceName)
                .setXid(transactionId)
                .setTimeout(timeout.toNanos())
                .setXatmiFlags(flags).build();
        CasualNWMessage serviceRequestNetworkMessage = CasualNWMessageImpl.of(corrid, serviceRequestMessage);
        LOG.finest(() -> "issuing service call request, corrid: " + PrettyPrinter.casualStringify(corrid) + SERVICE_NAME_LITERAL + serviceName);

        if(noReply)
        {
            connection.getNetworkConnection().requestNoReply(serviceRequestNetworkMessage);
            return Optional.empty();
        }
        return Optional.of(connection.getNetworkConnection().request(serviceRequestNetworkMessage));
    }

    private CasualNWMessage serviceDiscovery(UUID corrid, String serviceName)
    {
        LOG.finest(() -> "issuing domain discovery, corrid: " + PrettyPrinter.casualStringify(corrid) + SERVICE_NAME_LITERAL + serviceName);
        Domain domain = ConfigurationService.getInstance().getConfiguration().getDomain();
        CasualDomainDiscoveryRequestMessage requestMsg = CasualDomainDiscoveryRequestMessage.createBuilder()
                .setExecution(UUID.randomUUID())
                .setDomainId(domain.getId())
                .setDomainName(domain.getName())
                .setServiceNames(Arrays.asList(serviceName))
                .build();
        CasualNWMessage msg = CasualNWMessageImpl.of(corrid, requestMsg);
        CompletableFuture> replyMsgFuture = connection.getNetworkConnection().request(msg);

        CasualNWMessage replyMsg = replyMsgFuture.join();
        LOG.finest(() -> "domain discovery ok for corrid: " + PrettyPrinter.casualStringify(corrid) + SERVICE_NAME_LITERAL + serviceName);
        return replyMsg;
    }

    private boolean serviceExists(UUID corrid, String serviceName)
    {
        CasualNWMessage replyMsg = serviceDiscovery(corrid, serviceName);
        return replyMsg.getMessage().getServices().stream()
                .map(Service::getName)
                .anyMatch(v -> v.equals(serviceName));
    }

    private ServiceReturn toServiceReturn(CasualNWMessage v)
    {
        CasualServiceCallReplyMessage serviceReplyMessage = v.getMessage();
        return new ServiceReturn<>(serviceReplyMessage.getServiceBuffer(), (serviceReplyMessage.getError() == ErrorState.OK) ? ServiceReturnState.TPSUCCESS : ServiceReturnState.TPFAIL, serviceReplyMessage.getError(), serviceReplyMessage.getUserDefinedCode());
    }

    ServiceCallEventPublisher getEventPublisher()
    {
        if(eventPublisher == null)
        {
            UUID domainId = ConfigurationService.getInstance().getConfiguration().getDomain().getId();
            eventPublisher = ServiceCallEventPublisher.of(ServiceCallEventStoreFactory.getStore(domainId));
        }
        return eventPublisher;
    }

    void setEventPublisher(ServiceCallEventPublisher eventPublisher)
    {
        this.eventPublisher = eventPublisher;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy