org.bytesoft.bytejta.supports.springcloud.web.TransactionRequestInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for org.bytesoft.bytejta.supports.springcloud.web.TransactionRequestInterceptor.java

Source

/**
 * Copyright 2014-2017 yangming.liu<bytefox@126.com>.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, see <http://www.gnu.org/licenses/>.
 */
package org.bytesoft.bytejta.supports.springcloud.web;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.bytesoft.bytejta.TransactionImpl;
import org.bytesoft.bytejta.supports.rpc.TransactionRequestImpl;
import org.bytesoft.bytejta.supports.rpc.TransactionResponseImpl;
import org.bytesoft.bytejta.supports.springcloud.SpringCloudBeanRegistry;
import org.bytesoft.bytejta.supports.springcloud.loadbalancer.TransactionLoadBalancerInterceptor;
import org.bytesoft.bytejta.supports.wire.RemoteCoordinator;
import org.bytesoft.common.utils.ByteUtils;
import org.bytesoft.common.utils.CommonUtils;
import org.bytesoft.transaction.TransactionBeanFactory;
import org.bytesoft.transaction.TransactionContext;
import org.bytesoft.transaction.TransactionManager;
import org.bytesoft.transaction.archive.XAResourceArchive;
import org.bytesoft.transaction.aware.TransactionEndpointAware;
import org.bytesoft.transaction.supports.rpc.TransactionInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.HttpClientErrorException;

import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.Server.MetaInfo;

public class TransactionRequestInterceptor
        implements ClientHttpRequestInterceptor, TransactionEndpointAware, ApplicationContextAware {
    static final Logger logger = LoggerFactory.getLogger(TransactionRequestInterceptor.class);

    static final String HEADER_TRANCACTION_KEY = "org.bytesoft.bytejta.transaction";
    static final String HEADER_PROPAGATION_KEY = "org.bytesoft.bytejta.propagation";

    private String identifier;
    private ApplicationContext applicationContext;

    public ClientHttpResponse intercept(final HttpRequest httpRequest, byte[] body,
            ClientHttpRequestExecution execution) throws IOException {

        SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance();
        TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory();
        TransactionManager transactionManager = beanFactory.getTransactionManager();

        TransactionImpl transaction = //
                (TransactionImpl) transactionManager.getTransactionQuietly();

        URI uri = httpRequest.getURI();

        String path = uri.getPath();
        if (path.startsWith("/org/bytesoft/bytejta")) {
            return execution.execute(httpRequest, body);
        } else if (transaction == null) {
            return execution.execute(httpRequest, body);
        }

        final Map<String, XAResourceArchive> participants = transaction.getParticipantMap();
        beanRegistry.setLoadBalancerInterceptor(new TransactionLoadBalancerInterceptor() {
            public List<Server> beforeCompletion(List<Server> servers) {
                final List<Server> readyServerList = new ArrayList<Server>();
                final List<Server> unReadyServerList = new ArrayList<Server>();

                for (int i = 0; servers != null && i < servers.size(); i++) {
                    Server server = servers.get(i);
                    MetaInfo metaInfo = server.getMetaInfo();
                    String instanceId = metaInfo.getInstanceId();

                    if (participants.containsKey(instanceId)) {
                        List<Server> serverList = new ArrayList<Server>();
                        serverList.add(server);
                        return serverList;
                    } // end-if (participants.containsKey(instanceId))

                    if (server.isReadyToServe()) {
                        readyServerList.add(server);
                    } else {
                        unReadyServerList.add(server);
                    }

                }

                logger.warn("There is no suitable server: expect= {}, actual= {}!", participants.keySet(), servers);
                return readyServerList.isEmpty() ? unReadyServerList : readyServerList;
            }

            public void afterCompletion(Server server) {
                if (server == null) {
                    logger.warn(
                            "There is no suitable server, the TransactionInterceptor.beforeSendRequest() operation is not executed!");
                    return;
                } else {
                    try {
                        MetaInfo metaInfo = server.getMetaInfo();
                        String instanceId = metaInfo.getInstanceId();
                        invokeBeforeSendRequest(httpRequest, instanceId);
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
        });

        ClientHttpResponse httpResponse = null;
        boolean serverFlag = true;
        try {
            httpResponse = execution.execute(httpRequest, body);
            return httpResponse;
        } catch (HttpClientErrorException clientEx) {
            serverFlag = false;
            throw clientEx;
        } finally {
            beanRegistry.removeLoadBalancerInterceptor();

            if (httpResponse != null) {
                this.invokeAfterRecvResponse(httpResponse, serverFlag);
            } // end-if (httpResponse != null)

        }

    }

    private void invokeBeforeSendRequest(HttpRequest httpRequest, String identifier) throws IOException {
        SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance();
        TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory();
        TransactionManager transactionManager = beanFactory.getTransactionManager();
        TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor();

        TransactionImpl transaction = //
                (TransactionImpl) transactionManager.getTransactionQuietly();

        TransactionContext transactionContext = transaction.getTransactionContext();

        byte[] reqByteArray = CommonUtils.serializeObject(transactionContext);
        String reqTransactionStr = ByteUtils.byteArrayToString(reqByteArray);

        HttpHeaders reqHeaders = httpRequest.getHeaders();
        reqHeaders.add(HEADER_TRANCACTION_KEY, reqTransactionStr);
        reqHeaders.add(HEADER_PROPAGATION_KEY, this.identifier);

        TransactionRequestImpl request = new TransactionRequestImpl();
        request.setTransactionContext(transactionContext);
        RemoteCoordinator coordinator = beanRegistry.getConsumeCoordinator(identifier);
        request.setTargetTransactionCoordinator(coordinator);

        transactionInterceptor.beforeSendRequest(request);
    }

    private void invokeAfterRecvResponse(ClientHttpResponse httpResponse, boolean serverFlag) throws IOException {
        SpringCloudBeanRegistry beanRegistry = SpringCloudBeanRegistry.getInstance();
        TransactionBeanFactory beanFactory = beanRegistry.getBeanFactory();
        TransactionInterceptor transactionInterceptor = beanFactory.getTransactionInterceptor();

        HttpHeaders respHeaders = httpResponse.getHeaders();
        String respTransactionStr = respHeaders.getFirst(HEADER_TRANCACTION_KEY);
        String respPropagationStr = respHeaders.getFirst(HEADER_PROPAGATION_KEY);

        byte[] byteArray = ByteUtils.stringToByteArray(StringUtils.trimToNull(respTransactionStr));
        TransactionContext serverContext = (TransactionContext) CommonUtils.deserializeObject(byteArray);

        TransactionResponseImpl txResp = new TransactionResponseImpl();
        txResp.setTransactionContext(serverContext);
        RemoteCoordinator serverCoordinator = beanRegistry.getConsumeCoordinator(respPropagationStr);
        txResp.setSourceTransactionCoordinator(serverCoordinator);
        txResp.setParticipantDelistFlag(serverFlag ? false : true);

        transactionInterceptor.afterReceiveResponse(txResp);
    }

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public void setEndpoint(String identifier) {
        this.identifier = identifier;
    }

}