Package org.springframework.erlang.connection

Source Code of org.springframework.erlang.connection.SingleConnectionFactory$SharedConnectionInvocationHandler

/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.erlang.connection;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.erlang.OtpIOException;
import org.springframework.util.Assert;

import com.ericsson.otp.erlang.OtpAuthException;
import com.ericsson.otp.erlang.OtpPeer;
import com.ericsson.otp.erlang.OtpSelf;

/**
* A {@link ConnectionFactory} implementation that returns the same Connections from all
* {@link #createConnection()} calls, and ignores calls to {@link Connection#close()}.
*
* Provides a more traditional API to creating a connection to a remote erlang
* node than the JInterface API.
*
* <p>
* The following is taken from the JInterface javadocs that describe the valid
* node names that can be used. These naming constraints apply to the string
* values you pass into the node names in SimpleConnectionFactory's constructor.
* <p>
* About nodenames: Erlang nodenames consist of two components, an alivename and
* a hostname separated by '@'. Additionally, there are two nodename formats:
* short and long. Short names are of the form "alive@hostname", while long
* names are of the form "alive@host.fully.qualified.domainname". Erlang has
* special requirements regarding the use of the short and long formats, in
* particular they cannot be mixed freely in a network of communicating nodes,
* however Jinterface makes no distinction. See the Erlang documentation for
* more information about nodenames.
* </p>
*
* <p>
* The constructors for the AbstractNode classes will create names exactly as
* you provide them as long as the name contains '@'. If the string you provide
* contains no '@', it will be treated as an alivename and the name of the local
* host will be appended, resulting in a shortname. Nodenames longer than 255
* characters will be truncated without warning.
* </p>
*
* <p>
* Upon initialization, this class attempts to read the file .erlang.cookie in
* the user's home directory, and uses the trimmed first line of the file as the
* default cookie by those constructors lacking a cookie argument. If for any
* reason the file cannot be found or read, the default cookie will be set to
* the empty string (""). The location of a user's home directory is determined
* using the system property "user.home", which may not be automatically set on
* all platforms.
* </p>
*
* @author Mark Pollack
*/
public class SingleConnectionFactory implements ConnectionFactory,
    InitializingBean, DisposableBean {

  protected final Log logger = LogFactory.getLog(getClass());

  private boolean uniqueSelfNodeName = true;

  private final String selfNodeName;

  private String cookie;

  private final String peerNodeName;

  private OtpSelf otpSelf;

  private OtpPeer otpPeer;

  /** Raw JInterface Connection */
  private Connection targetConnection;

  /** Proxy Connection */
  private Connection connection;

  /** Synchronization monitor for the shared Connection */
  private final Object connectionMonitor = new Object();

  public SingleConnectionFactory(String selfNodeName, String cookie,
      String peerNodeName) {
    this.selfNodeName = selfNodeName;
    this.cookie = cookie;
    this.peerNodeName = peerNodeName;
  }

  public SingleConnectionFactory(String selfNodeName, String peerNodeName) {
    this.selfNodeName = selfNodeName;
    this.peerNodeName = peerNodeName;
  }

  public boolean isUniqueSelfNodeName() {
    return uniqueSelfNodeName;
  }

  public void setUniqueSelfNodeName(boolean uniqueSelfNodeName) {
    this.uniqueSelfNodeName = uniqueSelfNodeName;
  }

  @Override
  public Connection createConnection() throws UnknownHostException,
      OtpAuthException {
    synchronized (this.connectionMonitor) {
      if (this.connection == null) {
        try {
          initConnection();
        } catch (IOException e) {
          throw new OtpIOException("failed to connect from '"
              + this.selfNodeName + "' to peer node '"
              + this.peerNodeName + "'", e);
        }

      }
      return this.connection;
    }
  }

  public void initConnection() throws IOException, OtpAuthException {
    synchronized (this.connectionMonitor) {
      if (this.targetConnection != null) {
        closeConnection(this.targetConnection);
      }
      this.targetConnection = doCreateConnection();
      prepareConnection(this.targetConnection);
      if (logger.isInfoEnabled()) {
        logger.info("Established shared Rabbit Connection: "
            + this.targetConnection);
      }
      this.connection = getSharedConnectionProxy(this.targetConnection);
    }
  }

  /**
   * Close the underlying shared connection.
   * The provider of this ConnectionFactory needs to care for proper shutdown.
   * <p>As this bean implements DisposableBean, a bean factory will
   * automatically invoke this on destruction of its cached singletons.
   */
  @Override
  public void destroy() {
    resetConnection();
  }

  /**
   * Reset the underlying shared Connection, to be reinitialized on next access.
   */
  public void resetConnection() {
    synchronized (this.connectionMonitor) {
      if (this.targetConnection != null) {
        closeConnection(this.targetConnection);
      }
      this.targetConnection = null;
      this.connection = null;
    }
  }

  /**
   * Close the given Connection.
   *
   * @param connection
   *            the Connection to close
   */
  protected void closeConnection(Connection connection) {
    if (logger.isDebugEnabled()) {
      logger.debug("Closing shared Rabbit Connection: "
          + this.targetConnection);
    }
    try {
      // TODO there are other close overloads close(int closeCode,
      // java.lang.String closeMessage, int timeout)
      connection.close();
    } catch (Throwable ex) {
      logger.debug("Could not close shared Rabbit Connection", ex);
    }
  }

  /**
   * Create a JInterface Connection via this class's ConnectionFactory.
   *
   * @return the new Otp Connection
   * @throws OtpAuthException Any.
   * @throws IOException Any.
   */
  protected Connection doCreateConnection() throws IOException,
      OtpAuthException {
    return new DefaultConnection(otpSelf.connect(otpPeer));
  }

  protected void prepareConnection(Connection con) throws IOException {
  }

  /**
   * Wrap the given OtpConnection with a proxy that delegates every method
   * call to it but suppresses close calls. This is useful for allowing
   * application code to handle a special framework Connection just like an
   * ordinary Connection from a Rabbit ConnectionFactory.
   *
   * @param target
   *            the original Connection to wrap
   * @return the wrapped Connection
   */
  protected Connection getSharedConnectionProxy(Connection target) {
    List<Class<?>> classes = new ArrayList<Class<?>>(1);
    classes.add(Connection.class);
    return (Connection) Proxy.newProxyInstance(
        Connection.class.getClassLoader(),
        classes.toArray(new Class<?>[classes.size()]),
        new SharedConnectionInvocationHandler(target));
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
   */
  @Override
  public void afterPropertiesSet() {
    Assert.isTrue(this.selfNodeName != null || this.peerNodeName != null,
        "'selfNodeName' or 'peerNodeName' is required");
    String selfNodeNameToUse = this.selfNodeName;
    if (isUniqueSelfNodeName()) {
      selfNodeNameToUse = this.selfNodeName + "-" + UUID.randomUUID().toString();
      logger.debug("Creating OtpSelf with node name = [" + selfNodeNameToUse + "]");
    }
    try {
      if (this.cookie == null) {
        this.otpSelf = new OtpSelf(selfNodeNameToUse.trim());
      } else {
        this.otpSelf = new OtpSelf(selfNodeNameToUse.trim(), this.cookie);
      }
    } catch (IOException e) {
      throw new OtpIOException(e);
    }
    this.otpPeer = new OtpPeer(this.peerNodeName.trim());
  }

  private class SharedConnectionInvocationHandler implements
      InvocationHandler {

    private final Connection target;

    public SharedConnectionInvocationHandler(Connection target) {
      this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
      if (method.getName().equals("equals")) {
        // Only consider equal when proxies are identical.
        return (proxy == args[0]);
      } else if (method.getName().equals("hashCode")) {
        // Use hashCode of Connection proxy.
        return System.identityHashCode(proxy);
      } else if (method.getName().equals("toString")) {
        return "Shared Otp Connection: " + this.target;
      } else if (method.getName().equals("close")) {
        // Handle close method: don't pass the call on.
        return null;
      }
      try {
        return method.invoke(this.target, args);
      } catch (InvocationTargetException ex) {
        throw ex.getTargetException();
      }
    }
  }

}
TOP

Related Classes of org.springframework.erlang.connection.SingleConnectionFactory$SharedConnectionInvocationHandler

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.