Package org.hibernate.search.backend.jgroups.impl

Source Code of org.hibernate.search.backend.jgroups.impl.DispatchMessageSender

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat, Inc. and/or its affiliates or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat, Inc.
*
* 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, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*/
package org.hibernate.search.backend.jgroups.impl;

import java.net.URL;
import java.util.Properties;

import org.hibernate.search.backend.jgroups.logging.impl.Log;
import org.hibernate.search.engine.service.spi.ServiceManager;
import org.hibernate.search.spi.BuildContext;
import org.hibernate.search.util.configuration.impl.ConfigurationParseHelper;
import org.hibernate.search.util.logging.impl.LoggerFactory;
import org.jgroups.Address;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.MessageListener;
import org.jgroups.UpHandler;
import org.jgroups.View;
import org.jgroups.blocks.MessageDispatcher;
import org.jgroups.blocks.RequestOptions;
import org.jgroups.blocks.mux.MuxMessageDispatcher;
import org.jgroups.blocks.mux.Muxer;
import org.jgroups.util.Rsp;
import org.jgroups.util.RspList;

/**
* We use the MessageDispatcher instead of the JChannel to be able to use blocking
* operations (optionally) without having to rely on the RSVP protocol
* being configured on the stack.
*
* @author Lukasz Moren
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2012 Red Hat Inc.
* @author Ales Justin
* @author Hardy Ferentschik
*/
public final class DispatchMessageSender implements MessageSenderService {

  private static final Log log = LoggerFactory.make( Log.class );

  public static final String JGROUPS_PREFIX = "hibernate.search.services.jgroups.";
  public static final String CONFIGURATION_FILE = JGROUPS_PREFIX + "configurationFile";
  public static final String CLUSTER_NAME = JGROUPS_PREFIX + "clusterName";
  public static final String CHANNEL_INJECT = JGROUPS_PREFIX + "providedChannel";
  public static final String CLASSLOADER = JGROUPS_PREFIX + "classloader";
  public static final String MUX_ID = JGROUPS_PREFIX + "mux_id";

  private static final String DEFAULT_JGROUPS_CONFIGURATION_FILE = "flush-udp.xml";
  private static final String DEFAULT_CLUSTER_NAME = "Hibernate Search Cluster";

  private ChannelContainer channelContainer;
  private ServiceManager serviceManager;
  private MessageDispatcher dispatcher;

  @Override
  public Address getAddress() {
    return channelContainer.getChannel().getAddress();
  }

  @Override
  public View getView() {
    return channelContainer.getChannel().getView();
  }

  @Override
  public void send(final Message message, final boolean synchronous, final long timeout) throws Exception {
    final RequestOptions options = synchronous ? RequestOptions.SYNC() : RequestOptions.ASYNC();
    options.setExclusionList( dispatcher.getChannel().getAddress() );
    options.setTimeout( timeout );
    RspList<Object> rspList = dispatcher.castMessage( null, message, options );
    //JGroups won't throw these automatically as it would with a JChannel usage,
    //so we provide the same semantics by throwing the JGroups specific exceptions
    //as appropriate
    if ( synchronous ) {
      for ( Rsp rsp : rspList.values() ) {
        if ( !rsp.wasReceived() ) {
          if ( rsp.wasSuspected() ) {
            throw log.jgroupsSuspectingPeer( rsp.getSender() );
          }
          else {
            throw log.jgroupsRpcTimeout( rsp.getSender() );
          }
        }
        else {
          if ( rsp.hasException() ) {
            throw log.jgroupsRemoteException( rsp.getSender(), rsp.getException(), rsp.getException() );
          }
        }
      }
    }
  }

  @Override
  public void start(Properties props, BuildContext context) {
    log.jGroupsStartingChannelProvider();
    serviceManager = context.getServiceManager();

    channelContainer = buildChannel( props );
    channelContainer.start();

    NodeSelectorService masterNodeSelector = serviceManager.requestService( NodeSelectorService.class );
    JGroupsMasterMessageListener listener = new JGroupsMasterMessageListener( context, masterNodeSelector );

    JChannel channel = channelContainer.getChannel();

    UpHandler handler = channel.getUpHandler();
    if ( handler instanceof Muxer ) {
      Short muxId = (Short) props.get( MUX_ID );
      if ( muxId == null ) {
        throw log.missingJGroupsMuxId( DispatchMessageSender.MUX_ID );
      }
      @SuppressWarnings("unchecked")
      Muxer<UpHandler> muxer = (Muxer<UpHandler>) handler;
      if ( muxer.get( muxId ) != null ) {
        throw log.jGroupsMuxIdAlreadyTaken( muxId );
      }

      ClassLoader cl = (ClassLoader) props.get( CLASSLOADER );
      MessageListener wrapper = ( cl != null ) ? new ClassloaderMessageListener( listener, cl ) : listener;
      MessageListenerToRequestHandlerAdapter adapter = new MessageListenerToRequestHandlerAdapter( wrapper );
      dispatcher = new MuxMessageDispatcher( muxId, channel, wrapper, listener, adapter );
    }
    else {
      MessageListenerToRequestHandlerAdapter adapter = new MessageListenerToRequestHandlerAdapter( listener );
      dispatcher = new MessageDispatcher( channel, listener, listener, adapter );
    }

    masterNodeSelector.setLocalAddress( channel.getAddress() );

    if ( !channel.flushSupported() ) {
      log.jGroupsFlushNotPresentInStack();
    }
    if ( log.isDebugEnabled() ) {
      log.jgroupsFullConfiguration( channel.getProtocolStack().printProtocolSpecAsXML() );
    }
  }

  @Override
  public void stop() {
    serviceManager.releaseService( NodeSelectorService.class );
    serviceManager = null;
    dispatcher.stop();
    try {
      if ( channelContainer != null ) {
        channelContainer.close();
        channelContainer = null;
      }
    }
    catch (Exception toLog) {
      log.jGroupsClosingChannelError( toLog );
    }
  }

  /**
   * Reads configuration and builds channel with its base.
   * In order of preference - we first look for an external JGroups file, then a set of XML properties, and
   * finally the legacy JGroups String properties.
   *
   * @param props configuration file
   * @return the ChannelContainer to manage the JGroups JChannel
   */
  private static ChannelContainer buildChannel(Properties props) {
    final String clusterName = ConfigurationParseHelper.getString(
        props, DispatchMessageSender.CLUSTER_NAME, DEFAULT_CLUSTER_NAME );
    if ( props != null ) {
      final Object channelObject = props.get( DispatchMessageSender.CHANNEL_INJECT );
      if ( channelObject != null ) {
        try {
          return new InjectedChannelContainer( (org.jgroups.JChannel) channelObject );
        }
        catch (ClassCastException e) {
          throw log.jGroupsChannelInjectionError( DispatchMessageSender.CHANNEL_INJECT, e, channelObject.getClass() );
        }
      }

      final String cfg = props.getProperty( DispatchMessageSender.CONFIGURATION_FILE );
      if ( cfg != null ) {
        try {
          log.startingJGroupsChannel( cfg );
          return new ManagedChannelContainer( new JChannel( ConfigurationParseHelper.locateConfig( cfg ) ), clusterName );
        }
        catch (Exception e) {
          throw log.jGroupsChannelCreationUsingFileError( cfg, e );
        }
      }
    }

    log.jGroupsConfigurationNotFoundInProperties( props );
    try {
      URL fileUrl = ConfigurationParseHelper.locateConfig( DispatchMessageSender.DEFAULT_JGROUPS_CONFIGURATION_FILE );
      if ( fileUrl != null ) {
        log.startingJGroupsChannel( fileUrl );
        return new ManagedChannelContainer( new JChannel( fileUrl ), clusterName );
      }
      else {
        log.jGroupsDefaultConfigurationFileNotFound();
        return new ManagedChannelContainer( new JChannel(), clusterName );
      }
    }
    catch (Exception e) {
      throw log.unableToStartJGroupsChannel( e );
    }
  }

  private interface ChannelContainer {
    JChannel getChannel();
    void close();
    void start();
  }

  private static class ManagedChannelContainer implements ChannelContainer {
    private final JChannel channel;
    private final String clusterName;

    ManagedChannelContainer(JChannel channel, String clusterName) {
      if ( channel == null ) {
        throw new NullPointerException( "channel must not be null" );
      }
      if ( clusterName == null ) {
        throw new NullPointerException( "clusterName must not be null" );
      }
      this.channel = channel;
      this.clusterName = clusterName;
    }

    @Override
    public JChannel getChannel() {
      return channel;
    }

    @Override
    public void close() {
      log.jGroupsDisconnectingAndClosingChannel( clusterName );
      channel.disconnect();
      channel.close();
    }

    @Override
    public void start() {
      try {
        channel.connect( clusterName );
        log.jGroupsConnectedToCluster( clusterName, channel.getAddress() );
      }
      catch (Exception e) {
        throw log.unableConnectingToJGroupsCluster( clusterName, e );
      }
    }
  }

  private static class InjectedChannelContainer implements ChannelContainer {

    private final JChannel channel;

    InjectedChannelContainer(JChannel channel) {
      if ( channel == null ) {
        throw new NullPointerException( "channel must not be null" );
      }
      this.channel = channel;
    }

    @Override
    public JChannel getChannel() {
      return channel;
    }

    @Override
    public void close() {
      //No-Op
    }

    @Override
    public void start() {
      //No-Op
    }
  }

}
TOP

Related Classes of org.hibernate.search.backend.jgroups.impl.DispatchMessageSender

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.