/**
 * This file contains the {@link RequestDistributor},
 * {@link RequestDistributorGateway} and {@link RequestDistributorServer}
 * classes, which define a sample for the "Request Distributor" sample JGroups
 * application described in the "JGroups" report and presentation.
 * 
 * @author Savas Ali TOKMEN, http://ali.tokmen.com
 */

package com.tokmen.ali.java.jgroups;

import org.jgroups.*;
import java.util.Random;
import java.util.Vector;

/**
 * This is the main class of the Request Distributor application.
 * 
 * If {@link RequestDistributor#main(String[])} main is called with
 * <b>gateway</b> as argument then a gateway is created. Otherwise,
 * a server is created.
 *
 * For people who cannot access and/or read the JGroups report/presentation,
 * here's how the things basically works:
 * 
 * 		- The network is formed by one gateway (French name: <b>passerelle</b>
 * 		  or <b>point d'entrée au réseau</b>) and a group of servers (French
 * 		  name: <b>serveur</b>). The aim of the network is to respond to
 * 		  requests (French name: <b>requête</b>) coming from clients (French
 * 		  name: <b>client</b>). Of course, the network is kept together using
 * 		  the JGroups protocol.
 * 		- A client always sends its request to the gateway.
 * 		- The gateway in turn picks up a random server and sends the client's
 * 		  request to that server.
 * 		- The server then processes the request and responds to the client
 * 		  directly.
 * 
 * Since the target for this application is a French university, the report and
 * presentation are in French, so are the messages outputted by the software
 * and all comments inside methods.
 * 
 * It is expected for the report and presentation to become online on the
 * http://scholar.alishomepage.com/Master/JGroups/ website by the
 * 22<sup>nd</sup> of October, 2006.
 * 
 * @author Savas Ali TOKMEN, http://ali.tokmen.com
 * @version 2006.10.22
 */
public class RequestDistributor {
	/**
	 * Main method of the class.
	 * 
	 * @param args Arguments array.
	 */
	public static void main(String[] args) throws Exception {
		Channel channel = new JChannel(	"UDP:PING:FD:STABLE:NAKACK:UNICAST:" +
										"FRAG:FLUSH:GMS:VIEW_ENFORCER:STATE_TRANSFER:QUEUE");
		channel.connect("RequestDistributer");

		if( args.length > 0 && args[0].compareTo("gateway") == 0 ) {
			System.out.println("Mode passerelle");
			new RequestDistributorGateway(channel);
		} else {
			System.out.println("Mode serveur");
			new RequestDistributorServer(channel);
		}

		channel.disconnect();
		channel.close();
	}
}

/**
 * This class defines the Request Distributor Gateway. This is the object that
 * normally receives the client's requests; but for simplicity what we've done
 * is that the RequestDistributorGateway object directly generates requests.
 * 
 * @author Savas Ali TOKMEN, http://ali.tokmen.com
 * @version 2006.10.22
 */
class RequestDistributorGateway {
	Random random;
	Channel channel;

	/**
	 * Constructor: saves the channel, creates the random number generator and
	 * loops to generate a request every second. Each time a request is
	 * generated, {@link RequestDistributorGateway#OnRequest(String)} is called
	 * with a different argument (an increasing integer).
	 * 
	 * @param channel JGroups channel.
	 */
	public RequestDistributorGateway( Channel channel ) {
		int i = 0;
		this.channel = channel;
		random = new Random();

		// La passerelle simule aussi les clients
		while(true) {
			synchronized(this) {
				// Attendre une seconde entre chaque requête
				try { this.wait(1000); } catch (InterruptedException e) {}

				// Générer une requête
				System.out.println("Requete recue: "+(i++));
				try {
					OnRequest(Integer.toString(i));
				} catch (Exception e) {}
			}
		}
	}

	/**
	 * Called when a request is received. Will pick a server that should
	 * process the request and redirect the request to it.
	 * 
	 * @param request Received request.
	 */
	public void OnRequest( String request ) throws Exception {
		// Obtenir la liste des membres du groupe
		Vector<Address> servers = channel.getView().getMembers();

		if( servers.size() <= 1 ){
			// Dans ce cas là, il n'y a aucun serveur!
			System.out.println("Requete intraitable: aucun serveur present!");
		} else {
			int gatewayPosition = servers.indexOf(channel.getLocalAddress());
			int targetServer = gatewayPosition;

			// Sélectionner le serveur qui doit executer la requête en
			// vérifiant bien que c'est un serveur et pas le point d'entrée
			while ( targetServer  == gatewayPosition ) {
				targetServer = random.nextInt(servers.size());
			}

			// Rediriger la requête au serveur, qui lui répondra directement au client
			System.out.println("Il y a "+(servers.size()-1)+" serveurs, requete "+request+" redigirigee vers: "+servers.get(targetServer));
			channel.send(new Message( servers.get(targetServer), null, request ));
		}
	}
}

/**
 * This class defines a Request Distributor Server. This is the object to which
 * the gateway redirects requests and that will in turn process the request and
 * answer the client.
 * 
 * @author Savas Ali TOKMEN, http://ali.tokmen.com
 * @version 2006.10.22
 */
class RequestDistributorServer {
	/**
	 * Constructor: loops and waits for JGroups messages to arrive. Each time a
	 * message is received, unpacks it to find out the contained request and
	 * processes it. For this extremely simple application processing is simply
	 * outputting the request to the console.
	 * 
	 * @param channel JGroups channel.
	 */
	public RequestDistributorServer( Channel channel ){
		while( true ) {
			try {
				// Attendre pour une requête
				Object o = channel.receive(0);

				if( o instanceof Message ) {
					// Vérifier que c'est une requête
					o = ((Message) o).getObject();

					if( o instanceof String ) {
						// Traiter la requête
						System.out.println("Requete traitee: "+o);
					} else {
						// Juste pour du debug
						System.out.println("Requete bizarre: "+o);
					}
				}
			} catch (Exception e) {}
		}
	}
}
