org.petalslink.dsb.federation.core.server.DefaultPropagationStrategy.java Source code

Java tutorial

Introduction

Here is the source code for org.petalslink.dsb.federation.core.server.DefaultPropagationStrategy.java

Source

/**
 * PETALS: PETALS Services Platform Copyright (C) 2009 EBM WebSourcing
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or any later version.
 * 
 * This library 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 library; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 * 
 * Initial developer(s): EBM WebSourcing
 */
package org.petalslink.dsb.federation.core.server;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.petalslink.dsb.api.EndpointQuery;
import org.petalslink.dsb.api.MessageExchange;
import org.petalslink.dsb.api.ServiceEndpoint;
import org.petalslink.dsb.federation.api.FederationException;
import org.petalslink.dsb.federation.core.api.FederationServer;
import org.petalslink.dsb.federation.core.api.PropagationStrategy;

/**
 * Lookup will return the results from the first response. Invoke contains all
 * that it is needed to invoke the service.
 * 
 * @author chamerling - eBM WebSourcing
 * 
 */
public class DefaultPropagationStrategy implements PropagationStrategy {

    private final FederationServer federationServer;

    /**
     * The correspondance between message ID and client. On message response,
     * get the initial client from the map and send it back the message.
     */
    private final Map<String, String> idClientMap;

    /**
     * The intial client which did the request. The key is the message ID, the
     * value is the initial client.
     */
    private final Map<String, String> initialClientMap;

    private final Map<String, Set<ServiceEndpoint>> endpoints;

    private final Map<String, AtomicLong> latches;

    // private final ExecutorService executorService;

    private static Log logger = LogFactory.getLog(DefaultPropagationStrategy.class);

    private final ExecutorService executorService;

    /**
    * 
    */
    public DefaultPropagationStrategy(final FederationServer federationServer) {
        this.federationServer = federationServer;
        this.idClientMap = new ConcurrentHashMap<String, String>(100);
        this.endpoints = new ConcurrentHashMap<String, Set<ServiceEndpoint>>(100);
        this.latches = new ConcurrentHashMap<String, AtomicLong>(100);
        this.initialClientMap = new ConcurrentHashMap<String, String>(100);

        this.executorService = Executors.newFixedThreadPool(10);
    }

    /**
     * {@inheritDoc}
     */
    public String getName() {
        return DefaultPropagationStrategy.class.getCanonicalName();
    }

    /**
     * The clientId is the final client to invoke and not the source one!
     * {@inheritDoc}
     * 
     * @throws FederationException
     */
    public void invoke(MessageExchange message, String clientId, String id) throws FederationException {

        // store the message ID so that we can callback the client when the
        // response is there...
        // if (this.idClientMap.get(id) != null) {
        if ((this.initialClientMap.get(message.getId()) != null) && message.getStatus().equals("Done")) {
            // this is a response... So get the initial client and send it back
            // the response
            String initialClient = this.initialClientMap.remove(message.getId());
            if (logger.isInfoEnabled()) {
                logger.info("This is a response by " + clientId + " for initial client " + initialClient);
            }

            if (initialClient != null) {
                // send the response to the client
                this.invoke2(message, initialClient, id);
            } else {
                logger.warn("Can not find a valid initial client ID");
            }
        } else {
            // this is a call...
            // store the messageID and the initial clientID
            this.idClientMap.put(id, clientId);
            this.initialClientMap.put(message.getId(), clientId);
            String clientIdToInvoke = this.getClientToInvoke(message, clientId);
            if (logger.isInfoEnabled()) {
                logger.info("Found the client '" + clientIdToInvoke + "' to forward message to");
            }
            if (clientIdToInvoke == null) {
                throw new FederationException("Can not define the client to invoke from the initial message");
            }
            // TODO Fire and Forget for all calls!
            this.invoke2(message, clientIdToInvoke, id);
        }
    }

    private void invoke2(MessageExchange message, String clientId, String id) throws FederationException {
        if (logger.isInfoEnabled()) {
            logger.info("Got an invoke call for client = '" + clientId + "'");
        }
        // get the federation node and container to reach...
        org.petalslink.dsb.federation.core.api.FederationClient client = this.federationServer.getClient(clientId);

        if (client == null) {
            String msg = "Can not find a valid client from clientID='" + clientId + "'";
            logger.warn(msg);
            throw new FederationException(msg);
        }

        try {
            this.federationServer.getClientManager().getClient(client.getCallbackURL()).invoke(message,
                    this.federationServer.getName(), id);
        } catch (FederationException e) {
            e.printStackTrace();
        }
    }

    /**
     * {@inheritDoc}
     */
    public void lookup(EndpointQuery query, String clientId, String id) throws FederationException {
        if (logger.isDebugEnabled()) {
            logger.debug("Got lookup call from client '" + clientId + "'");
        }

        // FIXME : If there are not clients... What to do?
        // get all the connected clients
        Set<org.petalslink.dsb.federation.core.api.FederationClient> clients = this.federationServer.getClients();

        // store the number of requests (all minus the client)...
        AtomicLong counter = new AtomicLong(clients.size() - 1);

        if (counter.get() <= 0) {
            // one way call, if there are not enough client, we must call reply
            // ourselves
            this.submitEmptyReply(clientId, id);
        } else {
            // TODO : Define a global timeout !
            this.idClientMap.put(id, clientId);
            this.latches.put(id, counter);
            this.endpoints.put(id, new HashSet<ServiceEndpoint>());

            // dummy implementation, call all then aggregate all the results...
            for (org.petalslink.dsb.federation.core.api.FederationClient federationClient : clients) {
                // submit lookup in separate threads
                if (!clientId.equals(federationClient.getName())) {
                    this.submitLookup(query, id, counter, federationClient);
                } else {
                    if (logger.isInfoEnabled()) {
                        logger.info("Do not call the client which did the request");
                    }
                }
            }
        }
    }

    /**
     * @param query
     * @param id
     * @param counter
     * @param federationClient
     */
    private void submitLookup(final EndpointQuery query, final String id, final AtomicLong counter,
            final org.petalslink.dsb.federation.core.api.FederationClient federationClient) {
        Thread t = new Thread() {

            @Override
            public void run() {
                if (logger.isDebugEnabled()) {
                    logger.debug("Client " + federationClient.getName() + " with callback "
                            + federationClient.getCallbackURL());
                }
                try {
                    // TODO : Only the federation server can talk to this
                    // client!
                    DefaultPropagationStrategy.this.federationServer.getClientManager()
                            .getClient(federationClient.getCallbackURL())
                            .lookup(query, DefaultPropagationStrategy.this.federationServer.getName(), id);
                } catch (FederationException e) {
                    counter.decrementAndGet();
                }
            }
        };
        this.executorService.submit(t);
    }

    /**
     * @param clientId
     * @param id
     */
    private void submitEmptyReply(final String clientId, final String id) {
        if (logger.isInfoEnabled()) {
            logger.info("Submit empty reply");
        }
        Thread t = new Thread() {
            /**
             * {@inheritDoc}
             */
            @Override
            public void run() {
                try {
                    DefaultPropagationStrategy.this.lookupReply2(new HashSet<ServiceEndpoint>(), clientId, id);
                } catch (FederationException e) {
                    e.printStackTrace();
                }
            }
        };
        this.executorService.submit(t);
    }

    /**
     * {@inheritDoc}
     */
    public void lookupReply(Set<ServiceEndpoint> endpoints, String clientId, String id) throws FederationException {
        if (logger.isInfoEnabled()) {
            logger.info("Got a lookupReply call from client = '" + clientId + "'");
        }

        // aggregate response...
        if (endpoints != null) {
            // update the endpoints location...
            for (ServiceEndpoint serviceEndpoint : endpoints) {
                // add the current client ID from response to the domain path so
                // that the call to the endpoint can be routed to the right
                // federation client.
                serviceEndpoint.setSubdomainLocation(clientId + "/" + serviceEndpoint.getSubdomainLocation());
            }

            Set<ServiceEndpoint> endpointsBuffer = this.endpoints.get(id);
            if (endpointsBuffer != null) {
                endpointsBuffer.addAll(endpoints);
            }
        }

        AtomicLong counter = this.latches.get(id);
        long remain = counter.decrementAndGet();
        if (logger.isInfoEnabled()) {
            logger.info("Waiting for " + remain + " more response");
        }

        if (remain <= 0) {
            this.latches.remove(id);

            if (this.idClientMap.get(id) != null) {
                // this is a response... So get the initial client and send it
                // back
                // the response
                String initialClient = this.idClientMap.remove(id);
                if (logger.isInfoEnabled()) {
                    logger.info("This is a response by " + clientId + " for initial client " + initialClient);
                }
                // send the response to the client
                this.lookupReply2(this.endpoints.remove(id), initialClient, id);
            } else {
                // Failure, this is a one way call!
                if (logger.isInfoEnabled()) {
                    logger.info("Failure, can not find a client...");
                }
            }
        }
    }

    /**
     * Invoke reply on client
     * 
     * @param endpoints
     * @param id
     * @throws FederationException
     */
    private void lookupReply2(Set<ServiceEndpoint> endpoints, String initialClientId, String id)
            throws FederationException {
        if (logger.isInfoEnabled()) {
            logger.info("Send back the loopup reply to the initial client");
        }

        // get the federation node and container to reach...
        org.petalslink.dsb.federation.core.api.FederationClient client = this.federationServer
                .getClient(initialClientId);

        if (client == null) {
            throw new FederationException(
                    "Can not find a valid client stub from clientID='" + initialClientId + "'");
        }
        try {
            this.federationServer.getClientManager().getClient(client.getCallbackURL()).lookupReply(endpoints,
                    this.federationServer.getName(), id);
        } catch (FederationException e) {
            e.printStackTrace();
        }
    }

    /**
     * The client to invoke is described within the message exhange. This
     * message exchange has been normally filled by a process which get a
     * service endpoint reference from the federation.
     * 
     * @param message
     * @param clientId
     * @return
     */
    private String getClientToInvoke(MessageExchange message, String clientId) {
        if (logger.isInfoEnabled()) {
            logger.info("Client ID : " + clientId);
            logger.info("Message = " + message);
        }
        String result = null;
        if ((message != null) && (message.getEndpoint() != null)) {
            // get the next peer from the domain location
            result = message.getEndpoint().getSubdomainLocation().substring(0,
                    message.getEndpoint().getSubdomainLocation().indexOf('/'));
        }
        return result;
    }
}