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

net.sf.hajdbc.distributed.jgroups.JGroupsCommandDispatcher Maven / Gradle / Ivy

There is a newer version: 3.6.61
Show newest version
/*
 * HA-JDBC: High-Availability JDBC
 * Copyright (C) 2012  Paul Ferraro
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * 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 program.  If not, see .
 */
package net.sf.hajdbc.distributed.jgroups;

import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;

import net.sf.hajdbc.Messages;
import net.sf.hajdbc.distributed.Command;
import net.sf.hajdbc.distributed.CommandDispatcher;
import net.sf.hajdbc.distributed.Member;
import net.sf.hajdbc.distributed.MembershipListener;
import net.sf.hajdbc.distributed.Stateful;
import net.sf.hajdbc.logging.Level;
import net.sf.hajdbc.logging.Logger;
import net.sf.hajdbc.logging.LoggerFactory;
import net.sf.hajdbc.util.Objects;

import org.jgroups.*;
import org.jgroups.blocks.MessageDispatcher;
import org.jgroups.blocks.RequestHandler;
import org.jgroups.blocks.RequestOptions;
import org.jgroups.blocks.ResponseMode;
import org.jgroups.blocks.locking.LockService;
import org.jgroups.util.Rsp;

/**
 * A JGroups-based command dispatcher.
 * 
 * @author Paul Ferraro
 * @see org.jgroups.blocks.MessageDispatcher
 * @param  the execution context type
 */
public class JGroupsCommandDispatcher implements RequestHandler, CommandDispatcher, org.jgroups.MembershipListener, MessageListener
{
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	private final String id;
	private final long timeout;
	private final MessageDispatcher dispatcher;
	private final C context;
	private final AtomicReference viewReference = new AtomicReference();
	private final MembershipListener membershipListener;
	private final Stateful stateful;
	private final LockService lockService;
	
	/**
	 * Constructs a new ChannelCommandDispatcher.
	 * @param id the channel name
	 * @param channel a JGroups channel
	 * @param timeout the command timeout
	 * @param context the execution context
	 * @param stateful the state transfer handler
	 * @param membershipListener notified of membership changes
	 * @throws Exception if channel cannot be created
	 */
	public JGroupsCommandDispatcher(String id, Channel channel, long timeout, C context, Stateful stateful, MembershipListener membershipListener) throws Exception
	{
		this.id = id;
		this.context = context;
		this.stateful = stateful;
		this.membershipListener = membershipListener;
		this.lockService = new LockService(channel);
		this.dispatcher = new MessageDispatcher( channel, this, this, this);
		this.timeout = timeout;
	}

	public LockService getLockService() {
		return lockService;
	}

	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.Lifecycle#start()
	 */
	@Override
	public void start() throws Exception
	{
		Channel channel = this.dispatcher.getChannel();
		
		channel.setDiscardOwnMessages(false);
		
		// Connect and fetch state
		int failCount = 0;
		boolean connected = false;
		while(!connected) {
			try {
				channel.connect(this.id, null, 2000);
				connected = true;
			}catch (Exception e){
				failCount++;
				if(failCount>=5){
					throw e;
				}
			}
		}
	}

	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.Lifecycle#stop()
	 */
	@Override
	public void stop()
	{
		Channel channel = this.dispatcher.getChannel();
		
		if (channel.isOpen())
		{
			if (channel.isConnected())
			{
				channel.disconnect();
			}
			
			channel.close();
		}
	}

	@Override
	public  Map executeAll(Command command, Member... excludedMembers)
	{
		Message message = new Message(null, this.getLocalAddress(), Objects.serialize(command));
		RequestOptions options = new RequestOptions(ResponseMode.GET_ALL, this.timeout);

		if ((excludedMembers != null) && (excludedMembers.length > 0))
		{
			Address[] exclusions = new Address[excludedMembers.length];
			for (int i = 0; i < excludedMembers.length; ++i)
			{
				exclusions[i] = ((AddressMember) excludedMembers[i]).getAddress();
			}
			options.setExclusionList(exclusions);
		}

		try
		{
			Map> responses = this.dispatcher.castMessage(null, message, options);
			
			if (responses == null) return Collections.emptyMap();
			
			Map results = new TreeMap();
			
			for (Map.Entry> entry: responses.entrySet())
			{
				Rsp response = entry.getValue();
	
				results.put(new AddressMember(entry.getKey()), response.wasReceived() ? response.getValue() : null);
			}
			
			return results;
		}
		catch (Exception e)
		{
			return null;
		}
	}
	
	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.distributed.CommandDispatcher#execute(Command, Member)
	 */
	@Override
	public  R execute(Command command, Member member)
	{
		Message message = new Message(((AddressMember) member).getAddress(), this.getLocalAddress(), Objects.serialize(command));

		try
		{
			return this.dispatcher.sendMessage(message, new RequestOptions(ResponseMode.GET_ALL, this.timeout));
		}
		catch (Exception e)
		{
			this.logger.log(Level.WARN, e);
			return null;
		}
	}

	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.distributed.CommandDispatcher#getLocal()
	 */
	@Override
	public AddressMember getLocal()
	{
		return new AddressMember(this.getLocalAddress());
	}
	
	private Address getLocalAddress()
	{
		return this.dispatcher.getChannel().getAddress();
	}
	
	/**
	 * {@inheritDoc}
	 * @see net.sf.hajdbc.distributed.CommandDispatcher#getCoordinator()
	 */
	@Override
	public AddressMember getCoordinator()
	{
		return new AddressMember(this.getCoordinatorAddress());
	}

	private Address getCoordinatorAddress()
	{
		return this.dispatcher.getChannel().getView().getMembers().get(0);
	}

	/**
	 * {@inheritDoc}
	 * @see org.jgroups.blocks.RequestHandler#handle(org.jgroups.Message)
	 */
	@Override
	public Object handle(Message message)
	{
		Command command = Objects.deserialize(message.getRawBuffer());

		this.logger.log(Level.DEBUG, Messages.COMMAND_RECEIVED.getMessage(command, message.getSrc()));
		
		return command.execute(this.context);
	}

	/**
	 * {@inheritDoc}
	 * @see org.jgroups.MembershipListener#viewAccepted(org.jgroups.View)
	 */
	@Override
	public void viewAccepted(View view)
	{
		if(view instanceof MergeView) {
			new Thread() {
				public void run() {
					lockService.unlockAll();
					// stop all access to resources protected by l1, l2 or l3
					// every thread needs to re-acquire the locks it holds
				}
			}.start();
		}
		if (this.membershipListener != null)
		{
			View oldView = this.viewReference.getAndSet(view);
			
			for (Address address: view.getMembers())
			{
				if ((oldView == null) || !oldView.containsMember(address))
				{
					this.membershipListener.added(new AddressMember(address));
				}
			}
			
			if (oldView != null)
			{
				for (Address address: oldView.getMembers())
				{
					if (!view.containsMember(address))
					{
						this.membershipListener.removed(new AddressMember(address));
					}
				}
			}
		}
	}

	/**
	 * {@inheritDoc}
	 * @see org.jgroups.MessageListener#getState(java.io.OutputStream)
	 */
	@Override
	public void getState(OutputStream output) throws Exception
	{
		ObjectOutput out = new ObjectOutputStream(output);
		this.stateful.writeState(out);
		out.flush();
	}

	/**
	 * {@inheritDoc}
	 * @see org.jgroups.MessageListener#setState(java.io.InputStream)
	 */
	@Override
	public void setState(InputStream input) throws Exception
	{
		this.stateful.readState(new ObjectInputStream(input));
	}

	/**
	 * {@inheritDoc}
	 * @see org.jgroups.MembershipListener#suspect(org.jgroups.Address)
	 */
	@Override
	public void suspect(Address member)
	{
	}

	/**
	 * {@inheritDoc}
	 * @see org.jgroups.MembershipListener#block()
	 */
	@Override
	public void block()
	{
	}

	/**
	 * {@inheritDoc}
	 * @see org.jgroups.MembershipListener#unblock()
	 */
	@Override
	public void unblock()
	{
	}

	/**
	 * {@inheritDoc}
	 * @see org.jgroups.MessageListener#receive(org.jgroups.Message)
	 */
	@Override
	public void receive(Message message)
	{
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy