JBossJCA > JBoss5.1.0 > resource adapter > SSH2【XIANG】

keywords:  
Edit Topic

1. 概述

本文将改造AMS中的SSH adapter【AMS > basic.app > container > ssh-adapter
首先阅读【JBoss5.1.0 > JMX】了解JMX相关知识。本文会用到。
务必阅读”JBoss5.1.0 > resource adapter > 概述与汇总“,以了解JCA工作原理和流程。

为了揭露JCA使用陷阱,我们故意在 1-7 小节中插入了几个 bugs,然后我们在第7节末尾做了陷阱总结。最后我们在第8节中,修复这些bug。
    

按照上面的JCA架构以及代码重用基本准则,我们将创建以下项目:
(a) API
  • ybxiang-ssh2-ra-api: SSH Resource Adapter接口
  • ybxiang-ssh2-ejb-api: EJB(用于查询Resource Adapter)接口
(b) API实现和JMX application
  • ybxiang-ssh2-ra-app: SSH Resource Adapter接口的实现。需要借助ra.xml来安装Resource Adapter。
  • ybxiang-ssh2-ejb-app: EJB(用于查询Resource Adapter)接口的实现。可以用Java Annotation把EJB服务发布到JBoss,也可以用jboss-ejb.xml文件发布。
  • ybxiang-ssh2-jmx-app: 创建MBean(用于查询Resource Adapter的Connection),用jboss-service.xml文件部署该MBean到MBean Server中。
(c) 数据源
  • ybxiang-ssh2-ds: 数据源配置,用于把SSH Resource Adapter发布成JNDI服务,JNDI名字为:“java:/AmsSSHAdapter”

(d) 客户端
  • ybxiang-ssh2-ejb-client: 访问EJB,JNDI名字:SSH2Executor/remote
  • ybxiang-ssh2-jmx-client: Java程序访问JBoss JMX RMI Adapter。
这些项目之间的依赖关系如下:


      注:对应的VISIO 文件:ybxiang-ssh2-projects.dependencies.vsd
2. API代码

这一部分分析各个项目的源代码。
2.1 ybxiang-ssh2-ra-api

classes关系图:


注:VISIO绘图文件:ybxiang-ssh2-ra-api-class.relation.vsd

2.2 ybxiang-ssh2-ejb-api
这里仅仅实现了一个常用的方法。

package com.ybxiang.ssh2.ejb.api;

import javax.ejb.Remote;

import com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo;

@Remote
public interface ISSH2Executor{
    public static final String JNDI_NAME = "SSH2Executor/remote";
    public void executeWithoutWait(SSHConnectionRequestInfo info,String command,String directory);
}

3. APP代码




3.1 ybxiang-ssh2-ra-app

ra.xml

<?xml version="1.0" encoding="UTF-8"?>
<connector version="1.5"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd">
    <display-name>SSH2 Adapter</display-name>
    <vendor-name>Xiang YingBing</vendor-name>
    <eis-type>SSH2 System</eis-type>
    <resourceadapter-version>1.0</resourceadapter-version>
    <license>
        <description>LGPL</description>
        <license-required>false</license-required>
    </license>
    <resourceadapter>
        <resourceadapter-class>com.ybxiang.ssh2.ra.app.SSHResourceAdapter</resourceadapter-class>
        <outbound-resourceadapter>
            <connection-definition>
                <managedconnectionfactory-class>com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory</managedconnectionfactory-class>
                <!-- 
                <config-property>
                    <config-property-name>xxx</config-property-name>
                    <config-property-type>xxx</config-property-type>
                    <config-property-value>xxx</config-property-value>
                </config-property>
                -->
                <connectionfactory-interface>com.ybxiang.ssh2.ra.api.ISSHConnectionFactory</connectionfactory-interface>
                <connectionfactory-impl-class>com.ybxiang.ssh2.ra.app.SSHConnectionFactory</connectionfactory-impl-class>
                <connection-interface>com.ybxiang.ssh2.ra.api.ISSHConnection</connection-interface>
                <connection-impl-class>com.ybxiang.ssh2.ra.app.SSHConnection</connection-impl-class>
                <!--  <connection-impl-class>com.ybxiang.ssh2.ra.app.SSHManagedConnection</connection-impl-class> -->
            </connection-definition>
            <transaction-support>NoTransaction</transaction-support>
            <authentication-mechanism>
                <authentication-mechanism-type>BasicPassword</authentication-mechanism-type>
                <credential-interface>javax.resource.spi.security.PasswordCredential</credential-interface>
            </authentication-mechanism>
            <!-- <reauthentication-support>true</reauthentication-support> -->
            <reauthentication-support>false</reauthentication-support>
        </outbound-resourceadapter>
        <!--
        <security-permission>
            <description>...</description>
            <security-permission-spec>
            permission java.io.FilePermission "/tmp/db/fs_store/*", "read,write";
            </security-permission-spec>
        </security-permission>
        -->
    </resourceadapter>
</connector>


类关系图:




注:VISIO绘图文件:ybxiang-ssh2-ra-app-class.relation.vsd
SSHResourceAdapter.java

public class SSHResourceAdapter implements ResourceAdapter {
    public static final Logger log = Logger.getLogger(SSHResourceAdapter.class.getName());
    public void start(javax.resource.spi.BootstrapContext arg0)
            throws javax.resource.spi.ResourceAdapterInternalException{
        log.info("start() > arg0="+arg0);
        log.info("start() > You should initialize something here if necessary.");
    }
...
}

SSHManagedConnectionFactory.java


public class SSHManagedConnectionFactory implements ManagedConnectionFactory {
    public static final Logger log = Logger.getLogger(SSHManagedConnectionFactory.class.getName());

    // create Connection Factory
    public Object createConnectionFactory() throws ResourceException {
        log.info("createConnectionFactory()");
        return new SSHConnectionFactory(this, null);
    }

    public Object createConnectionFactory(ConnectionManager cm) throws ResourceException {
        log.info("createConnectionFactory("+cm+")");
        return new SSHConnectionFactory(this, cm);
    }

    // Managed Connection
    public ManagedConnection createManagedConnection(Subject arg0, ConnectionRequestInfo info)
            throws ResourceException {
        log.info("createManagedConnection("+arg0+","+info+")");
        return new SSHManagedConnection((SSHConnectionRequestInfo) info);
    }

    public ManagedConnection matchManagedConnections(Set set, Subject sbj, ConnectionRequestInfo info) throws ResourceException {
        SSHConnectionRequestInfo requestInfo = (SSHConnectionRequestInfo) info;
        log.info("matchManagedConnections("+set+"."+sbj+","+info+")");
        for (Object item : set) {
            SSHManagedConnection conn = (SSHManagedConnection) item;
            SSHConnectionRequestInfo info2 = conn.getConnectionRequestInfo();
            if(requestInfo.equals(info2)){
                return conn;
            }
        }
        log.warn("return no matched ManagedConnection (null)");
        return null;
    }

    public PrintWriter getLogWriter() throws ResourceException {
        log.info("getLogWriter()");
        return null;
    }

    public void setLogWriter(PrintWriter arg0) throws ResourceException {
        log.info("setLogWriter("+arg0+")");
    }
}
SSHConnectionFactory.java


public class SSHConnectionFactory implements ISSHConnectionFactory{
    public static final Logger log = Logger.getLogger(SSHConnectionFactory.class.getName());
   
    private Reference m_reference;
    private ManagedConnectionFactory managedConnectionFactory;
    private ConnectionManager connectionManager;
   
    public SSHConnectionFactory(ManagedConnectionFactory managedConnectionFactory,ConnectionManager connectionManager){
        log.info("SSHConnectionFactory constructor...");
        //super();
        this.managedConnectionFactory = managedConnectionFactory;
        this.connectionManager = connectionManager;

    }
   
    public ISSHConnection createConnection(SSHConnectionRequestInfo info) throws ResourceException{
        log.info("createConnection("+info+")");
        if (this.connectionManager == null) {
            log.debug("No connection manager found, returning non-pooled connection");
            return new SSHConnection(new SSHManagedConnection(info),info);
        }else{
            return (SSHConnection) connectionManager.allocateConnection(managedConnectionFactory, info);
        }
    }
   
   
    public Reference getReference() throws NamingException {
        log.info("getReference()");
        return m_reference;
    }       
    public void setReference(Reference reference) {
        log.info("setReference("+reference+")");
        m_reference = reference;
    }   

}

上面红色代码将调用SSHManagedConnectionFactory的方法:
public ManagedConnection createManagedConnection(Subject arg0, ConnectionRequestInfo info)

得到SSHManagedConnection后,执行SSHManagedConnection.getConnection(ConnectionRequestInfo info) :
    public Object getConnection(Subject arg0, ConnectionRequestInfo arg1) throws ResourceException {
        log.info("getConnection("+arg0+","+arg1+")");
        return new SSHConnection(this,info);
    }

SSHManagedConnection.java


public class SSHManagedConnection implements ManagedConnection {
    public static final Logger log = Logger.getLogger(SSHManagedConnection.class.getName());
    private static final String TRANSACTIONS_NOT_SUPPORTED_ERROR = "Transactions not supported";

    private final SSHConnectionRequestInfo info;
    private PrintWriter m_out;
    private List<ConnectionEventListener> listeners = new LinkedList<ConnectionEventListener>();

    public SSHManagedConnection(SSHConnectionRequestInfo info) throws ResourceException{
        log.info("{hashCode("+this.hashCode()+")} > "+"SSHManagedConnection("+info+")");
        this.info = info;
    }

    public Object getConnection(Subject arg0, ConnectionRequestInfo arg1) throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getConnection("+arg0+","+arg1+")");
        return new SSHConnection(this,info);
    }

    public void destroy() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"destroy()");       
    }

    public void cleanup() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"cleanup()");   
    }

    public void associateConnection(Object arg0) throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"associateConnection("+arg0+")");
    }

    public void addConnectionEventListener(ConnectionEventListener arg0) {
        log.info("{hashCode("+this.hashCode()+")} > "+"addConnectionEventListener("+arg0+")");
        listeners.add(arg0);
    }

    public void removeConnectionEventListener(ConnectionEventListener arg0) {
        log.info("{hashCode("+this.hashCode()+")} > "+"removeConnectionEventListener("+arg0+")");
        listeners.remove(arg0);
    }

    public javax.transaction.xa.XAResource getXAResource() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getXAResource()");
        throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED_ERROR);
    }

    public LocalTransaction getLocalTransaction() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getLocalTransaction()");
        throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED_ERROR);
    }

    public ManagedConnectionMetaData getMetaData() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getMetaData()");
        return new SSHManagedConnectionMetaData();
    }

    public PrintWriter getLogWriter() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getLogWriter()");
        return this.m_out;
    }

    public void setLogWriter(PrintWriter arg0) throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"setLogWriter(java.io.PrintWriter "+arg0+")");
        this.m_out = arg0;
    }
   
   
    //this method is called by SSHManagedConnectionFactory
    public SSHConnectionRequestInfo getConnectionRequestInfo() {
        log.info("{hashCode("+this.hashCode()+")} > "+"getConnectionRequestInfo()");
        return this.info;
    }
   
    //called by SSHConnection to close related event listeners
    public void closeEventListener(SSHConnection sshConnection){
      ConnectionEvent event = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
      event.setConnectionHandle(sshConnection);
      //
      Iterator<ConnectionEventListener> list = listeners.iterator();
      while (list.hasNext()) {
          ConnectionEventListener listener = (ConnectionEventListener) list.next();
          listener.connectionClosed(event);
      }           
    }   
}

SSHConnection.java


public class SSHConnection implements ISSHConnection{
    public static final Logger log = Logger.getLogger(SSHConnection.class.getName());
    
    private static final String CONNECTION_NOT_INITIALIZED_ERROR = "SSH connection not initalized";
    private static final String FINAL_CLOSE_ERROR = "Close in final block threw exception";

    private final SSHConnectionRequestInfo info;
    private ch.ethz.ssh2.Connection ethz_ssh2_connection;
    
    private SSHManagedConnection sshManagedConnection;
    
    public SSHConnection(SSHManagedConnection sshManagedConnection,SSHConnectionRequestInfo info)throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"SSHConnection constructor...");
        this.sshManagedConnection = sshManagedConnection;
        this.info = sshManagedConnection.getConnectionRequestInfo();
        try {
            initConnection();
        } catch (Exception e) {
            log.error(e);
            throw new ResourceException(e);
        }
    }
    
    public SSHCommandResult execute(String command, String directory,long timeout) {
        log.info("{hashCode("+this.hashCode()+")} > "+"execute("+command+", "+directory+","+timeout+")");
        try{
            Session sess = spawn(command,directory);
            return getSessionResults(sess,timeout);
        }catch(Exception e){
            log.error(e);
            return null;
        }
    }

    public void execute(ISSHProcessOutputHandler handler, String command, String directory) {
        log.info("{hashCode("+this.hashCode()+")} > "+"execute(ISSHProcessOutputHandler handler, String command, String directory)");
        try{
            Session sess = spawn(command,directory);
            getSessionResults(sess, handler);
        }catch(Exception e){
            log.error(e);
        }        
    }

    public void executeWithoutWait(String command, String directory) {
        log.info("{hashCode("+this.hashCode()+")} > "+"executeWithoutWait("+command+", "+directory+")");
        try{
            spawn(command,directory);
        }catch(Exception e){
            log.error(e);
        }    
    }

    /**
     * TODO:
     * should pass string(example:sessioinID) as parameter instead of ch.ethz.ssh2.Session,
     *        because ch.ethz.ssh2.Session does NOT implement java.io.
     */
    public boolean isDone(Object sess) {
        log.info("{hashCode("+this.hashCode()+")} > "+"isDone(Object sess)");
        return isSessionDone((Session)sess);
    }

    /**
     * This method will be called by JBoss automatically.
     */
    public void close() {
        log.info("{hashCode("+this.hashCode()+")} > "+"close()");
        sshManagedConnection.closeEventListener(this);
        //
        log.info("{hashCode("+this.hashCode()+")} > "+"Close SSH connection");
        if (ethz_ssh2_connection != null) {
            ethz_ssh2_connection.close();
            ethz_ssh2_connection = null;
        }          
    }

    /******************************[private helper methods ]******************************/   
    private void initConnection()throws Exception{
        log.debug("initConnection():Creating a SSH Connection with Public Key : " + info.keyFilePath + ", HostName:" + info.hostName);
       
        ethz_ssh2_connection = new ch.ethz.ssh2.Connection(info.hostName);
        ethz_ssh2_connection.connect();
        //
        if(info.keyFilePath!=null){
            log.debug("Authenticating SSH Connection with Public Key : " + info.keyFilePath);
            if (ethz_ssh2_connection.authenticateWithPublicKey(info.userName, new File(info.keyFilePath), info.keyFilePassword) == false){
                throw new Exception("Authentication failed.");   
            }              
        }else{
            ethz_ssh2_connection.authenticateWithPassword(info.userName,info.password);
        }            
    }
    /**
     * Caller should remember the returned Session if he want to get the result later(not now).
     */
    private Session spawn(String command, String directory) throws IOException {
        String newCommand = command;
        if(directory !=null ){
            newCommand = "cd "+directory+" && "+command;
        }
        //
        Session sess = null;
        if (ethz_ssh2_connection != null) {
            sess = ethz_ssh2_connection.openSession();
            log.debug("executing command " + newCommand);
            sess.execCommand(newCommand);

        } else {
            log.error(CONNECTION_NOT_INITIALIZED_ERROR);
            throw new IOException(CONNECTION_NOT_INITIALIZED_ERROR);
        }
        return sess;
    }   

    /******************************[static helper methods ]******************************/
    public static boolean isSessionDone(Session sess) {
        return (sess.getExitStatus() != null);
    }
    /**
     * @param outputHandler implemented by the caller
     */
    public static void getSessionResults(Session sess, ISSHProcessOutputHandler outputHandler) throws IOException {       
        try {
            outputHandler.processOutput(sess.getStdout(), sess.getStderr());
            sess.waitForCondition(ChannelCondition.EXIT_STATUS, 10000);
            int status = sess.getExitStatus();
            log.debug("status is " + status);
        } finally {
            if (sess != null) {
                try {
                    sess.close();
                } catch (Exception ignore) {
                    log.error(FINAL_CLOSE_ERROR);
                }
            }
        }
    }
    /**
     * Read stdout and error stream from session by yourself.
     * TODO: how to return result before timeout???
     */
    public static SSHCommandResult getSessionResults(Session sess,long timeout) throws IOException {       
        SSHCommandResult sshCmdRes = new SSHCommandResult("", -1);
        try {
            boolean testingTimeout = false;
            if(testingTimeout){
                sess.waitForCondition(ChannelCondition.EXIT_STATUS, timeout);
                int status = sess.getExitStatus();
                log.debug("status is " + status);     
                if(status==ChannelCondition.STDOUT_DATA||status==ChannelCondition.STDERR_DATA||status==ChannelCondition.TIMEOUT ){
                    return null;
                }else{
                    //Now, it is NOT timeout, Let's read result.
                }
            }
            //
            StringBuffer sbStdout = new StringBuffer();
            //1.read stdout
            InputStream stdout = new ch.ethz.ssh2.StreamGobbler(sess.getStdout());
            BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
            while (true) {
                String line = br.readLine();
                if (line == null) {
                    break;
                }
                sbStdout.append(line).append("\n");
            }
            log.debug("output " + sbStdout.toString());
            //2.read error stream
            InputStream errorStream = new StreamGobbler(sess.getStderr());
            BufferedReader errorBR = new BufferedReader(new InputStreamReader(errorStream));
            StringBuffer errorSB = new StringBuffer();
            while (true) {
                String line = errorBR.readLine();
                if (line == null) {
                    break;
                }
                errorSB.append(line).append("\n");
            }
            log.debug("Error " + errorSB.toString());
            //wait 10 seconds for status is set correctly by the session.
            sess.waitForCondition(ChannelCondition.EXIT_STATUS, 10000);
            int status = sess.getExitStatus();
            log.debug("status is " + status);
            //now return.
            sshCmdRes.status=status;
            sshCmdRes.result = sbStdout.toString();
            sshCmdRes.error = errorSB.toString();
        } finally {
            if (sess != null) {
                try {
                    sess.close();
                } catch (Exception ignore) {
                    log.error(FINAL_CLOSE_ERROR);
                }
            }
        }
        return sshCmdRes;
    }    

}

3.2 ybxiang-ssh2-jmx-app

只提供最简单的业务方法:
public void executeWithoutWait(SSHConnectionRequestInfo info, String command,String directory);


注意:JMX的接口和实现的定义需要遵守很多规矩,参见:JBoss5.1.0 > JMX

SSHServiceMBean.java

package com.ybxiang.ssh2.jmx;

import javax.naming.NamingException;

import com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo;

/**
 * All interface names of JMX MBeans must end with "MBean"
 * @author yingbinx
 *
 */
public interface SSHServiceMBean extends org.jboss.system.ServiceMBean{   
    public void executeWithoutWait(SSHConnectionRequestInfo info, String command,String directory);   
}



SSHService.java


package com.ybxiang.ssh2.jmx;

import javax.naming.InitialContext;

import com.ybxiang.ssh2.jmx.SSHServiceMBean;
import com.ybxiang.ssh2.ra.api.ISSHConnection;
import com.ybxiang.ssh2.ra.api.ISSHConnectionFactory;
import com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo;
import com.ybxiang.ssh2.ra.api.Logger;

public class SSHService extends org.jboss.system.ServiceMBeanSupport implements SSHServiceMBean {
    public static final Logger log = Logger.getLogger(SSHService.class.getName());

    public void executeWithoutWait(SSHConnectionRequestInfo info, String command, String directory) {
        ISSHConnection connection = null;
        try {
            InitialContext ctx = new InitialContext();
            Object ref = ctx.lookup(ISSHConnectionFactory.JNDI_NAME);//"java:/AmsSSHAdapter"
            
            ISSHConnectionFactory cf = (ISSHConnectionFactory) ref;
            connection = cf.createConnection(info);
            connection.executeWithoutWait(command,directory);
        } catch (Exception e) {
            log.error("Failed during JNDI access"+e);
        }finally{
            try{
                //connection.close();//JBoss will close it.
            }catch(Exception e){
                log.error("Failed during Close SSHConnection"+e);
            }
        }
    }
//TO BE CONTINUED
//    //you can override bellow life cycle mehtods:
//    @Override
//    public void create() throws Exception {
//        super.create();
//    }
//    @Override
//    public void start() throws Exception {
//        super.start();
//    }
//    @Override
//    public void stop() {
//        super.stop();
//    }
//    @Override
//    public void destroy() {
//        super.destroy();
//    }
//    //...
}
3.3 ybxiang-ssh2-ejb-app

Connection不执行close()的情况下,容器会自己帮忙执行!


SSH2Executor.java



package com.ybxiang.ssh2.ejb.app;

import javax.ejb.Stateless;
import javax.naming.InitialContext;

import org.apache.log4j.Logger;

import com.ybxiang.ssh2.ejb.api.ISSH2Executor;
import com.ybxiang.ssh2.ra.api.ISSHConnection;
import com.ybxiang.ssh2.ra.api.ISSHConnectionFactory;
import com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo;

@Stateless
public class SSH2Executor implements ISSH2Executor{
    Logger log = Logger.getLogger(SSH2Executor.class);
   
    public void executeWithoutWait(SSHConnectionRequestInfo info,String command,String directory){
        ISSHConnection connection = null;
        try {           
            InitialContext ctx = new InitialContext();
            Object ref = ctx.lookup(ISSHConnectionFactory.JNDI_NAME);//"java:/AmsSSHAdapter"
           
            ISSHConnectionFactory cf = (ISSHConnectionFactory) ref;
            connection = cf.createConnection(info);
            connection.executeWithoutWait(command,directory);

        } catch (Exception e) {
            log.error("Failed during JNDI access", e);
        }finally{
            try{
                //connection.close();//JBoss will close it.
            }catch(Exception e){
                log.error("Failed during Close SSHConnection", e);
            }
        }
    }
}
4. ybxiang-ssh2-ds 分析

注意,
  • JNDI名字为“AmsSSHAdapter”。
  • <rar-name>的值必须是真正部署的rar文件的完整名字,这里为“ybxiang-ssh2-ra-app.rar”


<!DOCTYPE connection-factories PUBLIC
          "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
          "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
<connection-factories>
    <no-tx-connection-factory>
        <jndi-name>AmsSSHAdapter</jndi-name>
        <rar-name>ybxiang-ssh2-ra-app.rar</rar-name>

        <connection-definition>com.ybxiang.ssh2.ra.api.ISSHConnectionFactory</connection-definition>
        <!--
        <config-property name="xxx" type="xxx">xxx</config-property>
        -->
        <min-pool-size>3</min-pool-size>
        <max-pool-size>4</max-pool-size>

        <blocking-timeout-millis>5000</blocking-timeout-millis>
        <idle-timeout-minutes>10</idle-timeout-minutes>
        <!-- <application-managed-security/> -->
    </no-tx-connection-factory>
</connection-factories>
5.Client 代码

这里。我们有3种方式来间接访问我们的SSH Resource Adapter。

把D:\java\jboss-5.1.0.GA\client中所有jars设置到ybxiang-ssh2-jmx-client的"java build path/Libraries"
把D:\java\jboss-5.1.0.GA\client中所有jars设置到ybxiang-ssh2-ejb-client的"java build path/Libraries"
5.1 WEB Browser访问JBOSS JMX HTML ADAPTER

不需要任何代码。演示在后面。
5.2  ybxiang-ssh2-jmx-client

package com.ybxiang.ssh2.jmx.client;

import java.util.Properties;

import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.ObjectName;
import javax.naming.InitialContext;

import org.jboss.jmx.adaptor.rmi.RMIAdaptor;

import com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo;

public class JMXBrowser {
    public static final String RMIAdaptor_JNDI_NAME = "jmx/invoker/RMIAdaptor";
    //
    public static final String JNDIViewMBean_ObjectName = "jboss:service=JNDIView";
    public static final String SSHServiceMBean_ObjectName = "com.ybxiang.ssh2:service=SSHService";
   
    public static void main(String[] args) throws Exception {
        //demoJNDIViewMBean();
        executeSSH2CommandAtHome();//在家时的工作环境
        //executeSSH2CommandAtCompany();//在公司时的环境
    }

    /************************[test methods]**************************/
    public static void demoJNDIViewMBean() throws Exception{
        printMBeanInfo(JNDIViewMBean_ObjectName);
        //////
        RMIAdaptor server = getJBossRMIAdaptor();       
        ObjectName objName = new ObjectName(JNDIViewMBean_ObjectName);
        // Invoke the list(boolean) op
        String[] sig = { "boolean" };
        Object[] opArgs = { Boolean.TRUE };
        Object result = server.invoke(objName, "list", opArgs, sig);
        //
        System.out.println("JNDIView.list(true) output:\n" + result);       
    }
   
    public static void executeSSH2CommandAtHome()throws Exception{
        printMBeanInfo(SSHServiceMBean_ObjectName);
        //
        //Invoke public void executeWithoutWait(SSHConnectionRequestInfo info, String command,String directory);
        RMIAdaptor server = getJBossRMIAdaptor();
        String[] sig = {
                SSHConnectionRequestInfo.class.getName(),
                "java.lang.String",
                "java.lang.String" };
        Object[] opArgs = {
                new SSHConnectionRequestInfo("192.168.1.101","root","ybxiang"),
                "mkdir ybxiangsshJMXtest",
                "/tmp"

                };
        Object result = server.invoke(
                new ObjectName(SSHServiceMBean_ObjectName),
                "executeWithoutWait",
                opArgs,
                sig);
    }
    public static void executeSSH2CommandAtCompany()throws Exception{
        printMBeanInfo(SSHServiceMBean_ObjectName);
        //
        //Invoke public void executeWithoutWait(SSHConnectionRequestInfo info, String command,String directory);
        RMIAdaptor server = getJBossRMIAdaptor();
        String[] sig = {
                SSHConnectionRequestInfo.class.getName(),
                "java.lang.String",
                "java.lang.String" };
        Object[] opArgs = {
                new SSHConnectionRequestInfo("135.251.246.180","ems00","emsems"),
                "mkdir ybxiangsshJMXtest",
                "/home/ems00"

                };
        Object result = server.invoke(
                new ObjectName(SSHServiceMBean_ObjectName),
                "executeWithoutWait",
                opArgs,
                sig);
    }

    /************************[static helper methods]**************************/
    public static InitialContext getJBossInitialContext() throws Exception{
        Properties properties = new Properties();
        properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
        properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
        properties.put("java.naming.provider.url","localhost:1099");
        return new InitialContext(properties);          
    }       
    public static RMIAdaptor getJBossRMIAdaptor()throws Exception{
        InitialContext ic = getJBossInitialContext();
        return (RMIAdaptor) ic.lookup(RMIAdaptor_JNDI_NAME);       
    }
    public static void printMBeanInfo(String strObjectName)throws Exception{
        RMIAdaptor server = getJBossRMIAdaptor();
        //
        ObjectName objName = new ObjectName(strObjectName);
        MBeanInfo info = server.getMBeanInfo(objName);
        System.out.println("Class: " + info.getClassName());

        MBeanOperationInfo[] opInfo = info.getOperations();
        System.out.println("Operations: ");
        for (int i = 0; i < opInfo.length; i++) {
            MBeanOperationInfo op = opInfo[i];

            String returnType = op.getReturnType();
            String opName = op.getName();
            System.out.print(" + " + returnType + " " + opName + "(");

            MBeanParameterInfo[] params = op.getSignature();
            for (int j = 0; j < params.length; j++) {
                MBeanParameterInfo paramInfo = params[j];

                String pname = paramInfo.getName();
                String type = paramInfo.getType();

                if (pname.equals(type)) {
                    System.out.print(type);
                } else {
                    System.out.print(type + " " + pname);
                }

                if (j < params.length - 1) {
                    System.out.print(',');
                }
            }
            System.out.println(")");
        }       
    }
}
5.3  ybxiang-ssh2-ejb-client

EJBClient.java

public class EJBClient {
    public static void main(String args[]) throws Exception {
        Context context = getInitialContext();  
        ISSH2Executor executor = (ISSH2Executor)context.lookup(ISSH2Executor.JNDI_NAME);
       
        executeAtHome(executor);//在家时的测试环境
        //executeAtCompany(executor);//在公司时的测试环境

    }
    public static InitialContext getInitialContext() throws Exception{
        Properties properties = new Properties();
        properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
        properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
        properties.put("java.naming.provider.url","localhost:1099");
        return new InitialContext(properties);          
    }
   
    public static void executeAtHome(ISSH2Executor executor)throws Exception{
        SSHConnectionRequestInfo info = new SSHConnectionRequestInfo("192.168.1.101","root","ybxiang");
        executor.executeWithoutWait(info, "mkdir ybxiangsshtest", "/tmp");
    }
    public static void executeAtCompany(ISSH2Executor executor)throws Exception{
        SSHConnectionRequestInfo info = new SSHConnectionRequestInfo("135.251.246.180","ems00","emsems");   
        executor.executeWithoutWait(info, "mkdir ybxiangsshtest", "/home/ems00");
    }   
}

6. 部署/日志

部署目录:D:\java\jboss-5.1.0.GA\server\all\deploy
6.1部署API和ganymed-ssh2-build210.jar jar包:



6.1.1 部署ganymed-ssh2-build210.jar

          把 ganymed-ssh2-build210.jar 拷贝到 D:\java\jboss-5.1.0.GA\server\all\lib 下面。


6.1.2 导出并部署ybxiang-ssh2-ra-api.jar
导出:



结果:


6.1.3 部署ybxiang-ssh2-ejb-api.jar
导出:



结果:


6.2 启动JBoss

日志如下:
...
02:26:13,781 INFO  [ServerImpl] JBoss (Microcontainer) [5.1.0.GA (build: SVNTag=JBoss_5_1_0_GA date=200905221634)] Started in 1m:30s:500ms
6.3 部署ybxiang-ssh2-ra-app.rar
导出:


结果:



相关日志:
02:28:49,578 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHResourceAdapter>start() > arg0=org.jboss.resource.deployers.RARDeployment@1133351
02:28:49,578 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHResourceAdapter>start() > You should initialize something here if necessary.

6.4部署ybxiang-ssh2-ds.xml
把ybxiang-ssh2-ds.xml拷贝到D:\java\jboss-5.1.0.GA\server\all\deploy 就可以了。

相关日志:
02:30:52,828 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>setLogWriter(org.jboss.logging.util.LoggerPluginWriter@1121216)
02:30:52,843 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>createConnectionFactory(org.jboss.resource.connectionmanager.BaseConnectionManager2$ConnectionManagerProxy@9df864)
02:30:52,859 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>SSHConnectionFactory constructor...
02:30:52,859 INFO  [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=AmsSSHAdapter' to JNDI name 'java:AmsSSHAdapter'


提示:
java:AmsSSHAdapter”查询出来的reference是“SSHConnectionFactory
6.5 部署ybxiang-ssh2-ejb-app.jar

02:33:23,187 INFO  [Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@370280{vfszip:/D:/java/jboss-5.1.0.GA/server/all/deploy/ybxiang-ssh2-ejb.jar/}
02:33:23,187 INFO  [Ejb3DependenciesDeployer] Encountered deployment AbstractVFSDeploymentContext@370280{vfszip:/D:/java/jboss-5.1.0.GA/server/all/deploy/ybxiang-ssh2-ejb.jar/}
02:33:23,328 INFO  [JBossASKernel] Created KernelDeployment for: ybxiang-ssh2-ejb.jar
02:33:23,328 INFO  [JBossASKernel] installing bean: jboss.j2ee:jar=ybxiang-ssh2-ejb.jar,name=SSH2Executor,service=EJB3
02:33:23,328 INFO  [JBossASKernel]   with dependencies:
02:33:23,328 INFO  [JBossASKernel]   and demands:
02:33:23,328 INFO  [JBossASKernel]     jboss.ejb:service=EJBTimerService
02:33:23,328 INFO  [JBossASKernel]   and supplies:
02:33:23,328 INFO  [JBossASKernel]     Class:com.ybxiang.ssh2.ejb.api.ISSH2Executor
02:33:23,328 INFO  [JBossASKernel]     jndi:SSH2Executor/remote-com.ybxiang.ssh2.ejb.api.ISSH2Executor
02:33:23,328 INFO  [JBossASKernel]     jndi:SSH2Executor/remote
02:33:23,328 INFO  [JBossASKernel] Added bean(jboss.j2ee:jar=ybxiang-ssh2-ejb.jar,name=SSH2Executor,service=EJB3) to KernelDeployment of: ybxiang-ssh2-ejb.jar
02:33:23,328 INFO  [EJB3EndpointDeployer] Deploy AbstractBeanMetaData@18be861{name=jboss.j2ee:jar=ybxiang-ssh2-ejb.jar,name=SSH2Executor,service=EJB3_endpoint bean=...}
02:33:23,390 INFO  [SessionSpecContainer] Starting jboss.j2ee:jar=ybxiang-ssh2-ejb.jar,name=SSH2Executor,service=EJB3
02:33:23,390 INFO  [EJBContainer] STARTED EJB: com.ybxiang.ssh2.ejb.app.SSH2Executor ejbName: SSH2Executor
02:33:23,406 INFO  [JndiSessionRegistrarBase] Binding the following Entries in Global JNDI:

    SSH2Executor/remote - EJB3.x Default Remote Business Interface
    SSH2Executor/remote-com.ybxiang.ssh2.ejb.api.ISSH2Executor - EJB3.x Remote Business Interface


提示:

  • jboss.j2ee:jar=ybxiang-ssh2-ejb.jar,name=SSH2Executor,service=EJB3”这是EJB对应的JMX service的名字。
  • 其它EJB如果依赖这个EJB,那么可以用Java注解或者在xml配置文件中用<depends>jboss.j2ee:jar=ybxiang-ssh2-ejb.jar,name=SSH2Executor,service=EJB3</depends>元素指定依赖。

6.6 导出并部署ybxiang-ssh2-jmx-app
导出到临时目录“D:”,然后另外命名后缀为为sar的文件:




7. client 访问

7.1 web 浏览器

用web浏览器打开地址:http://localhost:8080/jmx-console/,如下:

点击"service=SSHService"连接,进入页面:




由 于“p1 com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo” 没有设置PropertyMapper,所以无法直接把string传递进去转化为 com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo对象。
TODO:可以解决这个问题。
7.2 JMX Client访问

日志:

[xiang]: 连接工厂首次被调用创建连接,
..INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>createConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)

[xiang]: 下面将按照ybxiang-ssh2-ds.xml的定义连续创建3个ManagedConnection,在第一个ManagedConnection创建完毕后,立刻让它创建SSHConnection

[xiang]:#1 SSHManagedConnection
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>createManagedConnection(null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14538030)} > SSHManagedConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)//构造函数
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14538030)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@1d6f2be[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@ddd52e handles=0 lastUse=1309783924265 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@daa846 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@15db93c])

[xiang]:在第一个ManagedConnection创建完毕后,立刻让它创建SSHConnection
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14538030)} > getConnection(null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)

[xiang]:#2 SSHManagedConnection

..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>createManagedConnection(null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(1059336)} > SSHManagedConnection(hostName=192.168.1.101;userName=root; keyFilePath=null;keyFilePassword=null)//构造函数
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(1059336)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@137a99e[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@102a08 handles=0 lastUse=1309783924265 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@daa846 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@15db93c])

[xiang]:#3 SSHManagedConnection
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>createManagedConnection(null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(15506763)} > SSHManagedConnection(hostName=192.168.1.101;userName=root; keyFilePath=null;keyFilePassword=null)//构造函数
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(15506763)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@4ad8a3[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@ec9d4b handles=0 lastUse=1309783924265 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@daa846 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@15db93c])

[xiiang]:第一个ManagedConnection 构造的 SSHConnection,
         该SSHConnection又在initConnection()方法中构造真正的对资源访问的连接,这里为ch.ethz.ssh2.Connection。

..INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(1655734)} > SSHConnection constructor...
..INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14538030)} > getConnectionRequestInfo()
..DEBUG: com.ybxiang.ssh2.ra.app.SSHConnection>initConnection():Creating a SSH Connection with Public Key : null, HostName:192.168.1.101
..INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(1655734)} > executeWithoutWait(mkdir ybxiangsshJMXtest, /tmp)
..DEBUG: com.ybxiang.ssh2.ra.app.SSHConnection>executing command cd /tmp && mkdir ybxiangsshJMXtest


总共3个
SSHManagedConnection,hashCode:
14538030
1059336
15506763

再次运行:

20:58:30,140 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>createConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
20:58:30,140 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>matchManagedConnections([com.ybxiang.ssh2.ra.app.SSHManagedConnection@ec9d4b].null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
[xiang]: 我们可以看见,SSHManagedConnectionFactory现在查询旧的SSHManagedConnection,而不是继续构造 SSHManagedConnection!!!没有看见SSHManagedConnection的构造函数被调用!


[xiang]:从下面的代码可以看出,查询出的旧的SSHManagedConnection(15506763)被挑选出来,创建SSHConnection。
20:58:30,140 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(15506763)} > getConnectionRequestInfo()
20:58:30,140 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(15506763)} > getConnection(null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
20:58:30,140 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(24953499)} > SSHConnection constructor...
20:58:30,140 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(15506763)} > getConnectionRequestInfo()
20:58:30,140 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHConnection>initConnection():Creating a SSH Connection with Public Key : null, HostName:192.168.1.101
20:58:30,500 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(24953499)} > executeWithoutWait(mkdir ybxiangsshJMXtest, /tmp)
20:58:30,500 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHConnection>executing command cd /tmp && mkdir ybxiangsshJMXtest

结果:
-bash-3.2$ ls
ams  deployIDM.sh  Desktop  eclipse  maven  SET.AMS_ENV.sh  tempfile  test  tmp  workspace  ybxiangsshJMXtest
-bash-3.2$

成功!
闲置测试:

已知:
第一次运行时,SSHManagedConnection(14538030)被取用
第一次运行时,SSHManagedConnection(15506763)被取用


因为ybxiang-ssh2-ds.xml 中配置有<idle-timeout-minutes>10</idle-timeout-minutes>元素,所以我们等待10分钟,可以看见jboss server打印出日志:

21:04:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(1059336)} > destroy()
[xiang:] 从上面的日志可以看出,第三个SSHManagedConnection(1059336)一直没有被使用过。它的闲置时间最长。它首先被销毁了!


[xiang:] 从下面的日志可以看出,SSHManagedConnectionFactory立刻又创建了1个SSHManagedConnection,连接池里面保持3个!

21:04:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>createManagedConnection(null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
21:04:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(19745928)} > SSHManagedConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)//构造函数
21:04:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(19745928)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@fbb1d8[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@12d4c88 handles=0 lastUse=1309784671203 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@daa846 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@15db93c])

继续闲置半小时:

[xiang:]最后创建的那个SSHManagedConnection(19745928)一直没有使用过,所以首先销毁它。
21:19:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(19745928)} > destroy()

[xiang:]立即又创建一个(6387368),使pool里面保持最低3个。
21:19:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>createManagedConnection(null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
21:19:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(6387368)} > SSHManagedConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
21:19:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(6387368)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@b5441b[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@6176a8 handles=0 lastUse=1309785571203 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@daa846 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@15db93c])

[xiang:]最后创建的那个SSHManagedConnection(6387368)一直没有使用过,销毁它。
21:34:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(6387368)} > destroy()

[xiang:]立即又创建一个(26679346),使pool里面保持最低3个。
21:34:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>createManagedConnection(null,hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
21:34:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(26679346)} > SSHManagedConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
21:34:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(26679346)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@b57c75[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@1971832 handles=0 lastUse=1309786471203 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@daa846 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@15db93c])

[xiang:]最后创建的那个SSHManagedConnection(26679346)一直没有使用过,销毁它。
21:49:31,203 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(26679346)} > destroy()
...
循环!


我们发现,我们最初占用的2个SSHManagedConnection(14538030)和SSHManagedConnection(15506763)一直没有被销毁,因为我们一直没有释放它!这就是性能被耗尽的一个根源。

最后,我们再连续运行JMXBrowser 2次,最后一次发现,无法得到连接了:

22:16:50,250 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>createConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
22:16:55,250 ERROR [STDERR] ERROR: com.ybxiang.ssh2.jmx.SSHService>Failed during JNDI accessjavax.resource.ResourceException: Unable to get managed connection for AmsSSHAdapter

这正好验证了,最大pool size为4!
这个问题会在后面解决:调用SSHManagedConnection.cleanup()能使得对应的SSHManagedConnection被放回connection pool。
关闭JBoss:

...
2011-07-04 22:22:09,515 INFO  [org.jboss.ejb3.session.SessionSpecContainer] (JBoss Shutdown Hook) Stopping jboss.j2ee:jar=ybxiang-ssh2-ejb-app.jar,name=SSH2Executor,service=EJB3
2011-07-04 22:22:09,531 INFO  [org.jboss.ejb3.EJBContainer] (JBoss Shutdown Hook) STOPPED EJB: com.ybxiang.ssh2.ejb.app.SSH2Executor ejbName: SSH2Executor
2011-07-04 22:22:10,734 INFO  [org.jboss.resource.connectionmanager.ConnectionFactoryBindingService] (JBoss Shutdown Hook) Unbound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=JmsXA' from JNDI name 'java:JmsXA'
...
2011-07-04 22:22:13,062 INFO  [org.jboss.resource.connectionmanager.ConnectionFactoryBindingService] (JBoss Shutdown Hook) Unbound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=AmsSSHAdapter' from JNDI name 'java:AmsSSHAdapter'
...
2011-07-04 22:22:13,125 INFO  [STDOUT] (JBoss Shutdown Hook) INFO: com.ybxiang.ssh2.ra.app.SSHResourceAdapter>stop() > You should destory something here if necessary.
...

可以发现,没有被放回连接池的SSHManagedConnection,没有被执行cleanup()/destroy() 方法!



现在,我们应认识到 不应该在SSHConnection里面执行 对真正的连向资源的连接(ch.ethz.ssh2.Connection)的 管理(创建/清理)动作,而是在SSHManagedConnection里面的destroy()里面执行!SSHConnection只是业务连接。 我们在后面修复该问题。
7.3 运行EJB client

重新部署ybxiang-ssh2-ds.xml【从deploy目录删除,然后重新拷贝进来】

运行EJBClient.java时,我们发现server端日志比上面的JMXBrowser执行时的日志多出下面一段(注意,我们没有显式的在代码中执行SSHConnection.close()方法):
...
16:11:42,406 INFO  [CachedConnectionManager] Closing a connection for you.  Please close them yourself: com.ybxiang.ssh2.ra.app.SSHConnection@1664dc8
java.lang.Throwable: STACKTRACE
    at org.jboss.resource.connectionmanager.CachedConnectionManager.registerConnection(CachedConnectionManager.java:278)
    at org.jboss.resource.connectionmanager.BaseConnectionManager2.allocateConnection(BaseConnectionManager2.java:524)
    at org.jboss.resource.connectionmanager.BaseConnectionManager2$ConnectionManagerProxy.allocateConnection(BaseConnectionManager2.java:941)
    at com.ybxiang.ssh2.ra.app.SSHConnectionFactory.createConnection(SSHConnectionFactory.java:34)
    at com.ybxiang.ssh2.ejb.app.SSH2Executor.executeWithoutWait(SSH2Executor.java:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    ...
16:11:42,406 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>close()

16:11:42,406 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>cleanup()

16:11:42,406 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>Close SSH connection
我们的代码中,没有任何一句 显式的(declaratively) 调用 SSHConnection.close(),为什么该方法会被调用呢?下面我们进行测试。

现在我们把该SSHConnection.close()注解掉,把对应的接口方法ISSHConnection.close()也注解掉。

(1) 重新部署,步骤如下:

    * 关闭jboss
    * 部署ybxiang-ssh2-ra-api.jar到D:\java\jboss-5.1.0.GA\server\all\lib
    * 部署ybxiang-ssh2-ra-app.rar到D:\java\jboss-5.1.0.GA\server\all\deploy
    * 重新启动JBoss

(2) 现在我们在运行EJBClient.java,JBoss Server日志如下:
...
19:09:32,156 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>SSHConnection constructor...
19:09:32,156 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>getConnectionRequestInfo()
19:09:32,156 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHConnection>initConnection():Creating a SSH Connection with Public Key : null, HostName:192.168.1.101
19:09:33,718 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>executeWithoutWait(mkdir ybxiangsshtest, /tmp)
19:09:33,734 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHConnection>executing command cd /tmp && mkdir ybxiangsshtest
19:09:33,750 INFO  [CachedConnectionManager] Could not find a close method on alleged connection objects.  Please close your own connections.


原来是JBoss Server主动寻找SSHConnection是否存在叫做close()的方法:
    *   如果没有找到,则打印出上面红色消息
    *   如果找到了,则调用对应SSHConnection instance的close()方法。然后打印出对应的STACKTRACE(前面已经展示过)。



(3)运行 JMXBrowser 结果如下:
...
19:29:16,531 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>SSHConnection constructor...
19:29:16,531 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>getConnectionRequestInfo()
19:29:16,531 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHConnection>initConnection():Creating a SSH Connection with Public Key : null, HostName:192.168.1.101
19:29:16,828 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>executeWithoutWait(mkdir ybxiangsshJMXtest, /tmp)
19:29:16,843 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHConnection>executing command cd /tmp && mkdir ybxiangsshJMXtest

我们发现,通过JMX MBean 运行的时候,得到的SSHConnection,JBoss是不会主动帮助关闭的!




(4) 关于从ConnectionFactory获取的Connection(这里为SSHConnection)的close()方法的总结:

  • 必须设置一个方法,用于关闭真实的对Resource的连接【这里为ch.ethz.ssh2.Connection】。
  • 通过EJBClient调用EJB,再从ConnectionFactory获取Connection(这里为SSHConnection)这种情况下,使用完毕该Connection后,
(a) 如果想要让JBoss自动帮助关闭它【JBOSS在EJB的方法(通常是一个transaction)执行完毕后执行关闭动作,这是合理的】,那么必须让该Connection的接口及其实现带有一个叫做close()的方法;
(b) 如果想要自己写代码关闭从ConnectionFactory获取Connection(这里为SSHConnection),那么该关闭方法的名字可以随意。
  • 通过JMX调 用MBean,再从ConnectionFactory获取Connection(这里为SSHConnection)这种情况下,使用完毕该 Connection后,JBoss永远 不会 帮助关闭该connection,本人猜测原因为JBoss无法判定该MBean的某方法 是否在代表了一个完整的transaction,如果该方法只是transaction的一部分,这时候JBoss帮助执行了该Connection(这 里为SSHConnection)的close()方法,那会造成不良后果。

(5) 本人建议:
         为了保证从ConnectionFactory获取的Connection被回收,必须将它显式的(declarative)关闭。这样代码也便于移植到其它j2ee容器中。上面的STACKTRACE就是对用户的一种警告!执行该Connection的关闭动作时:
         (a) 可以 关闭 对资源的真正的连接(这里为ch.ethz.ssh2.Connection)
         (b) 可以 把 对资源的真正的连接(这里为ch.ethz.ssh2.Connection) 放回 connection pool中,并非真正的关闭之。下一次调用的时候,再分配出去。
7.4 测试:不使用叫做close()的方法来关闭,而是用testMethod4Close()执行:

7.4.1 测试: 不释放底层ch.ethz.ssh2.Connection连接。仅仅执行sshManagedConnection.cleanup();


(1) 我们为ISSHConnection新增一个接口方法:
public void testMethod4Close();

(2) 然后在SSHConnection中实现它:
    public void testMethod4Close(){
        try{
            sshManagedConnection.cleanup();
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > testMethod4Close() Exception: "+e);
        }       
    }

(3) 在SSH2Executor / SSHService中,调用它:
  
    public void executeWithoutWait(SSHConnectionRequestInfo info,String command,String directory){
        ISSHConnection connection = null;
        try {           
            InitialContext ctx = new InitialContext();
            Object ref = ctx.lookup(ISSHConnectionFactory.JNDI_NAME);//"java:/AmsSSHAdapter"
           
            ISSHConnectionFactory cf = (ISSHConnectionFactory) ref;
            connection = cf.createConnection(info);
            connection.executeWithoutWait(command,directory);
        } catch (Exception e) {
            log.error("Failed during JNDI access", e);
        }finally{
            try{
                connection.testMethod4Close();
                //connection.close();//JBoss will close it automatically.//TODO: restore this line after test is finished.
            }catch(Exception e){
                log.error("Failed during Close SSHConnection", e);
            }
        }
   }


(4) 关闭JBOss
(5) 重新部署一切改动过代码的模块:
ybxiang-ssh2-ra-api.jar
ybxiang-ssh2-ra-app
ybxiang-ssh2-ejb-app
ybxiang-ssh2-jmx-app

(6) 重新启动JBoss。
下面是运行JMXBrowser 5次的结果:
23:05:20,875 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>createConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
23:05:25,875 ERROR [STDERR] ERROR: com.ybxiang.ssh2.jmx.SSHService>Failed during JNDI accessjavax.resource.ResourceException: Unable to get managed connection for AmsSSHAdapter
23:05:25,890 ERROR [STDERR] ERROR: com.ybxiang.ssh2.jmx.SSHService>Failed during Close SSHConnectionjava.lang.NullPointerException

该日志表明:还是没有回收SSHManagedConnection!
 
原 因:利用SSHManagedConnection获取的SSHConnection instance因为没有释放 ch.ethz.ssh2.Connection,所以JBoss server无法销毁该instance。这样SSHManagedConnection 就无法被放回连接池。

下面测试close真正的底层connection。
下面是运行JMXBrowser 5次的结果:
23:05:20,875 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>createConnection(hostName=192.168.1.101;userName=root;keyFilePath=null;keyFilePassword=null)
23:05:25,875 ERROR [STDERR] ERROR: com.ybxiang.ssh2.jmx.SSHService>Failed during JNDI accessjavax.resource.ResourceException: Unable to get managed connection for AmsSSHAdapter
23:05:25,890 ERROR [STDERR] ERROR: com.ybxiang.ssh2.jmx.SSHService>Failed during Close SSHConnectionjava.lang.NullPointerException

还是没有回收SSHManagedConnection!
 

TODO:测试close真正的底层connection。
7.4.2 测试关闭底层的ch.ethz.ssh2.Connection

(1) 修改 SSHConnection.testMethod4Close()方法如下:

    public void testMethod4Close(){
        try{
            log.info("{hashCode("+this.hashCode()+")} > "+"Close SSH connection");
            if (ethz_ssh2_connection != null) {
                ethz_ssh2_connection.close();
                ethz_ssh2_connection = null;
            } 

           
            //sshManagedConnection.cleanup();
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > testMethod4Close() Exception: "+e);
        }       
    }


(2) 重新部署 ybxiang-ssh2-ra-app.rar【如果不生效,那么完整的部署一遍所有的模块】
(3) 运行JMXBrowser 5次,第5次日志:

09:01:52,577 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>createConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
09:01:57,611 ERROR [STDERR] ERROR: com.ybxiang.ssh2.jmx.SSHService>Failed during JNDI accessjavax.resource.ResourceException: Unable to get managed connection for AmsSSHAdapter
09:01:57,611 ERROR [STDERR] ERROR: com.ybxiang.ssh2.jmx.SSHService>Failed during Close SSHConnection java.lang.NullPointerException


这表明,SSHManagedConnection还是没有被回收。
7.4.3 测试关闭底层的ch.ethz.ssh2.Connection并调用SSHManagedConnection.cleanup()


(1) SSHConnection.testMethod4Close()改成如下:
    public void testMethod4Close(){
        try{
            log.info("{hashCode("+this.hashCode()+")} > "+"Close SSH connection");
            if (ethz_ssh2_connection != null) {
                ethz_ssh2_connection.close();
                ethz_ssh2_connection = null;
            } 
           
            sshManagedConnection.cleanup();

        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > testMethod4Close() Exception: "+e);
        }       
    }

(2)重新部署 ybxiang-ssh2-ra-app.rar


(3) 运行JMXBrowser 5次

结果还是无法回收 SSHManagedConnection。
7.4.4 测试 不关闭底层的ch.ethz.ssh2.Connection,只调用SSHManagedConnection.closeEventListener(SSHConnection)

(1) SSHConnection.testMethod4Close()修改如下
    public void testMethod4Close(){
        log.info("{hashCode("+this.hashCode()+")} > "+"testMethod4Close()");
        try{
            log.info("sshManagedConnection.closeEventListener(this);");
            sshManagedConnection.closeEventListener(this);           
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > testMethod4Close() Exception: "+e);
        }       
    }

(2) 重新部署 ybxiang-ssh2-ra-app.rar
第一次日志:

...INFO...SSHConnectionFactory>createConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnectionFactory>createManagedConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnection>{hashCode(18922657)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnection>{hashCode(18922657)} > addConnectionEventListener...
...INFO...SSHManagedConnection>{hashCode(18922657)} > getConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnectionFactory>createManagedConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHConnection>{hashCode(14222114)} > SSHConnection constructor...
...INFO...SSHManagedConnection>{hashCode(10563793)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnection>{hashCode(18922657)} > getConnectionRequestInfo()
...INFO...SSHManagedConnection>{hashCode(10563793)} > addConnectionEventListener....
...DEBUG...SSHConnection>initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
...INFO...SSHManagedConnectionFactory>createManagedConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnection>{hashCode(5063592)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnection>{hashCode(5063592)} > addConnectionEventListener...
...INFO...SSHConnection>{hashCode(14222114)} > executeWithoutWait(mkdir ybxiangsshJMXtest, /home/ems00)
...DEBUG...SSHConnection>executing command cd /home/ems00 && mkdir ybxiangsshJMXtest
...INFO...SSHConnection>{hashCode(14222114)} > testMethod4Close()
...INFO...SSHConnection>sshManagedConnection.closeEventListener(this);
...INFO...SSHManagedConnection>{hashCode(18922657)} > cleanup()


(a) 构造了3个SSHManagedConnection,hashcode分别为:
18922657
10563793
5063592
(b) 用SSHManagedConnection(18922657)构造了 SSHConnection(14222114)
(c) 新构造的SSHConnection在SSHManagedConnection中清理掉 该SSHConnection对应的ConnectionEventListener之后,被SSHManagedConnection.cleanup()被JBoss自动调用以回收该SSHManagedConnection到连接池!


第2次日志:

...INFO...SSHConnectionFactory>createConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnectionFactory>matchManagedConnections([com.ybxiang.ssh2.ra.app.SSHManagedConnection@120bca1].null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnection>{hashCode(18922657)} > getConnectionRequestInfo()
...INFO...SSHManagedConnection>{hashCode(18922657)} > getConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHConnection>{hashCode(16092196)} > SSHConnection constructor...
...INFO...SSHManagedConnection>{hashCode(18922657)} > getConnectionRequestInfo()
...DEBUG...SSHConnection>initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
...INFO...SSHConnection>{hashCode(16092196)} > executeWithoutWait(mkdir ybxiangsshJMXtest, /home/ems00)
...DEBUG...SSHConnection>executing command cd /home/ems00 && mkdir ybxiangsshJMXtest
...INFO...SSHConnection>{hashCode(16092196)} > testMethod4Close()
...INFO...SSHConnection>sshManagedConnection.closeEventListener(this);
...INFO...SSHManagedConnection>{hashCode(18922657)} > cleanup()



(a) SSHConnectionFactory 找出 匹配的 SSHManagedConnection(18922657),可以看出,还是第一次执行的那个SSHManagedConnection!
(b) SSHManagedConnection(18922657)构造出新的SSHConnection(16092196)
(c) 新构造的SSHConnection在SSHManagedConnection中清理掉 该SSHConnection对应的ConnectionEventListener之后,被SSHManagedConnection.cleanup()被JBoss自动调用以回收该SSHManagedConnection到连接池!
第3次日志:

...INFO...SSHConnectionFactory>createConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnectionFactory>matchManagedConnections([com.ybxiang.ssh2.ra.app.SSHManagedConnection@120bca1].null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHManagedConnection>{hashCode(18922657)} > getConnectionRequestInfo()
...INFO...SSHManagedConnection>{hashCode(18922657)} > getConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
...INFO...SSHConnection>{hashCode(2621958)} > SSHConnection constructor...
...INFO...SSHManagedConnection>{hashCode(18922657)} > getConnectionRequestInfo()
...DEBUG...SSHConnection>initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
...INFO...SSHConnection>{hashCode(2621958)} > executeWithoutWait(mkdir ybxiangsshJMXtest, /home/ems00)
...DEBUG...SSHConnection>executing command cd /home/ems00 && mkdir ybxiangsshJMXtest
...INFO...SSHConnection>{hashCode(2621958)} > testMethod4Close()
...INFO...SSHConnection>sshManagedConnection.closeEventListener(this);
...INFO...SSHManagedConnection>{hashCode(18922657)} > cleanup()


(a) SSHConnectionFactory 找出 匹配的 SSHManagedConnection(18922657),可以看出,还是第一次执行的那个SSHManagedConnection!
(b) SSHManagedConnection(18922657)构造出新的SSHConnection(2621958)
(c) 新构造的SSHConnection在SSHManagedConnection中清理掉 该SSHConnection对应的ConnectionEventListener之后,被SSHManagedConnection.cleanup()被JBoss自动调用以回收该SSHManagedConnection到连接池!
第4,5,6...次日志:

(a) SSHConnectionFactory 找出 匹配的 SSHManagedConnection(18922657),可以看出,还是第一次执行的那个SSHManagedConnection!
(b) SSHManagedConnection(18922657)构造出新的SSHConnection(...)
(c) 新构造的SSHConnection在SSHManagedConnection中清理掉 该SSHConnection对应的ConnectionEventListener之后,被SSHManagedConnection.cleanup()被JBoss自动调用以回收该SSHManagedConnection到连接池!

可以发现:SSHManagedConnection(18922657)的确被回收到了连接池!!!
请到本文最后,看看“总结”,我们将根据该总结,对我们的程序进行改进:参见接下来的 第8节 内容。
8. 改进


8.1 ISSHConnection
修改的方法:
(a) executeWithoutWait(...) 方法等价于异步执行,不会立刻返回结果。它会返回一个String类型的SESSION_ID,便于用于根据它查询当前工作状态。
(b) isDone(String sessionID) 传入参数改成String,用户应该传递SESSION ID以查询当前状态。

新增方法:
    public SSHCommandResult getResult(String sessionID); 如果该SESSION ID的工作已经结束,那么返回执行结果。
    public boolean closeSession(String sessionID);//中断该session。


package com.ybxiang.ssh2.ra.api;

public interface ISSHConnection{
    /**
     * Session will be closed finally.
     */
    public SSHCommandResult execute(String command, String directory,long timeout);
    /**
     * Session will be closed finally. ISSHProcessOutputHandler will get the result.
     */
    public void execute(ISSHProcessOutputHandler handler, String command, String directory);
    /**
     * Session will NOT be closed, so return sessionID for caller to close it later.
     * return SESSION ID
     */
    public String executeWithoutWait(String command, String directory);
    public boolean isDone(String sessionID);
    public SSHCommandResult getResult(String sessionID);
    public void closeSession(String sessionID);
   
    /**
     * Physicall connection(ch.ethz.ssh2.Connection) will NOT be closed because executeWithoutWait(...) may be using it.
     * this method will be called by JBoss EJB container automatically, however you should call it declaratively for good portability.
     */
    public void close();
}
8.2 SSHConnection.java

所有方法直接调用 sshManagedConnection 对应的方法。



public class SSHConnection implements ISSHConnection{
    public static final Logger log = Logger.getLogger(SSHConnection.class.getName());
   
    private SSHManagedConnection sshManagedConnection;
   
    public SSHConnection(SSHManagedConnection sshManagedConnection,SSHConnectionRequestInfo info)throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"SSHConnection constructor...");
        this.sshManagedConnection = sshManagedConnection;

    }
    /**
     * Session will be closed finally.
     */   
    public SSHCommandResult execute(String command, String directory,long timeout){
        log.info("{hashCode("+this.hashCode()+")} > "+"execute("+command+","+directory+","+timeout+")");
        return sshManagedConnection.execute(command, directory, timeout);
    }
   ...
}
8.3 SSHPhysicalConnectionOperator.java

该class利用ch.ethz.ssh2.Connection实现对SSH Server真正的底层的物理连接。
用它作为SSHManagedConnection的父类!


package com.ybxiang.ssh2.ra.app;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;

import javax.resource.ResourceException;

import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;

import com.ybxiang.ssh2.ra.api.ISSHProcessOutputHandler;
import com.ybxiang.ssh2.ra.api.Logger;
import com.ybxiang.ssh2.ra.api.SSHCommandResult;
import com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo;

public abstract class SSHPhysicalConnectionOperator {
    public static final Logger log = Logger.getLogger(SSHPhysicalConnectionOperator.class.getName());
    public static final long TIMEOUT_WAITFOR_RESULT = 10000;//10 seconds
   
    private static final String CONNECTION_NOT_INITIALIZED_ERROR = "SSH connection not initalized";
    private static final String FINAL_CLOSE_ERROR = "Close in final block threw exception";

    private SSHConnectionRequestInfo info;
    private ch.ethz.ssh2.Connection ethz_ssh2_connection;
    private HashMap<String,Session> sessions = new HashMap<String,Session>();

   
    public SSHPhysicalConnectionOperator(SSHConnectionRequestInfo info) throws ResourceException{
        log.info("{hashCode("+this.hashCode()+")} > "+"SSHPhysicalConnectionOperator() constructor...");
        try {
            this.info = info;
            initConnection();
        } catch (Exception e) {
            log.error("{hashCode("+this.hashCode()+")} > "+e);
            throw new ResourceException(e);
        }       
    }

   
    /******************************[private helper methods ]******************************/   
     private void initConnection()throws IOException{
        log.debug("{hashCode("+this.hashCode()+")} > "+"initConnection():Creating a SSH Connection with Public Key : " + info.keyFilePath + ", HostName:" + info.hostName);
       
        ethz_ssh2_connection = new ch.ethz.ssh2.Connection(info.hostName);
        ethz_ssh2_connection.connect();
        //
        if(info.keyFilePath!=null){
            log.debug("{hashCode("+this.hashCode()+")} > "+"Authenticating SSH Connection with Public Key : " + info.keyFilePath);
            if (ethz_ssh2_connection.authenticateWithPublicKey(info.userName, new File(info.keyFilePath), info.keyFilePassword) == false){
                throw new IOException("Authentication failed.");    
            }               
        }else{
            ethz_ssh2_connection.authenticateWithPassword(info.userName,info.password);
        }
    }    
    private ch.ethz.ssh2.Connection getEthzSsh2Connection()throws IOException{
        if(ethz_ssh2_connection==null){
            initConnection();
        }
        //
        return ethz_ssh2_connection;
    }
    private Session spawn(String command, String directory) throws IOException {
        String newCommand = command;
        if(directory !=null ){
            newCommand = "cd "+directory+" && "+command;
        }
        //
        Session sess = null;
        if (getEthzSsh2Connection() != null) {
            sess = getEthzSsh2Connection().openSession();            
            sessions.put(getSessionID(sess),sess);//managed connection
            
            log.debug("{hashCode("+this.hashCode()+")} > "+"executing command " + newCommand);
            sess.execCommand(newCommand);
        } else {
            log.error("{hashCode("+this.hashCode()+")} > "+CONNECTION_NOT_INITIALIZED_ERROR);
            throw new IOException(CONNECTION_NOT_INITIALIZED_ERROR);
        }
        return sess;
    }    

    /******************************[public final methods: should NOT be overridden]******************************/
    /**
     * Session will be closed finally.
     */
    public final SSHCommandResult execute(String command, String directory,long timeout) {
        log.info("{hashCode("+this.hashCode()+")} > "+"execute("+command+", "+directory+","+timeout+")");
        try{
            return getSessionResults(spawn(command,directory),timeout);
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > "+e);
            return null;
        }
    }
    /**
     * Session will be closed finally.
     */
    public final void execute(ISSHProcessOutputHandler handler, String command, String directory) {
        log.info("{hashCode("+this.hashCode()+")} > "+"execute(ISSHProcessOutputHandler handler, String command, String directory)");
        try{
            Session sess = spawn(command,directory);
            getSessionResults(sess, handler);
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > "+e);
        }       
    }

    /**
     * Session will NOT be closed, so return sessionID for caller to close it later.
     */
    public final String executeWithoutWait(String command, String directory) {
        log.info("{hashCode("+this.hashCode()+")} > "+"executeWithoutWait("+command+", "+directory+")");
        try{
            Session s = spawn(command,directory);
            return getSessionID(s);
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > "+e);
            return null;
        }   
    } 
    public final boolean isDone(String sessionID){
        Session s = sessions.get(sessionID);
        return s.getExitStatus()!=null;
    }
    public final SSHCommandResult getResult(String sessionID){
        try{
            Session s = sessions.get(sessionID);
            return getSessionResults(s,TIMEOUT_WAITFOR_RESULT);           
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > "+e);
            return null;
        }
    }
    public final void closeSession(String sessionID){
        try{
            Session s = sessions.get(sessionID);
            s.close();       
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > "+e);
        }       
    }
   
    public final void close(){
        log.info("{hashCode("+this.hashCode()+")} > "+"Close SSH connection");
        try{
            if (getEthzSsh2Connection() != null) {
                getEthzSsh2Connection().close();
                this.ethz_ssh2_connection = null;
            }              
        }catch(Exception e){
            log.error("{hashCode("+this.hashCode()+")} > "+"Close SSH connection Exception:"+e);
        }
    }  
    /******************************[static helper methods ]******************************/
     public static String getSessionID(Session s){
        return String.valueOf(s.hashCode());
    }
   
    /**
     * @param outputHandler implemented by the caller
     */
    public static void getSessionResults(Session sess, ISSHProcessOutputHandler outputHandler) throws IOException {       
        try {
            outputHandler.processOutput(sess.getStdout(), sess.getStderr());
            sess.waitForCondition(ChannelCondition.EXIT_STATUS, TIMEOUT_WAITFOR_RESULT);
            int status = sess.getExitStatus();
            log.debug("{session.hashCode("+sess+")} >"+"status is " + status);
        } finally {
            if (sess != null) {
                try {
                    sess.close();
                } catch (Exception ignore) {
                    log.error("{session.hashCode("+sess+")} >"+FINAL_CLOSE_ERROR);
                }
            }
        }
    }


    /**
     * Read stdout and error stream from session by yourself.
     * TODO: how to return result before timeout???
     */
    public static SSHCommandResult getSessionResults(Session sess,long timeout) throws IOException {        
        SSHCommandResult sshCmdRes = new SSHCommandResult("", -1);
        try {
            boolean testingTimeout = false;
            if(testingTimeout){
                sess.waitForCondition(ChannelCondition.EXIT_STATUS, timeout);
                int status = sess.getExitStatus();
                log.debug("{session.hashCode("+sess+")} >"+"status is " + status);      
                if(status==ChannelCondition.STDOUT_DATA||status==ChannelCondition.STDERR_DATA||status==ChannelCondition.TIMEOUT ){
                    return null;
                }else{
                    //Now, it is NOT timeout, Let's read result.
                }
            }
            //
            StringBuffer sbStdout = new StringBuffer();
            //1.read stdout
            InputStream stdout = new ch.ethz.ssh2.StreamGobbler(sess.getStdout());
            BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
            while (true) {
                String line = br.readLine();
                if (line == null) {
                    break;
                }
                sbStdout.append(line).append("\n");
            }
            log.debug("{session.hashCode("+sess+")} >"+"output " + sbStdout.toString());
            //2.read error stream
            InputStream errorStream = new StreamGobbler(sess.getStderr());
            BufferedReader errorBR = new BufferedReader(new InputStreamReader(errorStream));
            StringBuffer errorSB = new StringBuffer();
            while (true) {
                String line = errorBR.readLine();
                if (line == null) {
                    break;
                }
                errorSB.append(line).append("\n");
            }
            log.debug("{session.hashCode("+sess+")} >"+"Error " + errorSB.toString());
            //wait 10 seconds for status is set correctly by the session.
            sess.waitForCondition(ChannelCondition.EXIT_STATUS, 10000);
            int status = sess.getExitStatus();
            log.debug("{session.hashCode("+sess+")} >"+"status is " + status);
            //now return.
            sshCmdRes.status=status;
            sshCmdRes.result = sbStdout.toString();
            sshCmdRes.error = errorSB.toString();
        } finally {
            if (sess != null) {
                try {
                    sess.close();
                } catch (Exception ignore) {
                    log.error("{session.hashCode("+sess+")} >"+FINAL_CLOSE_ERROR);
                }
            }
        }
        return sshCmdRes;
    }     
}
8.4 SSHManagedConnection.java



package com.ybxiang.ssh2.ra.app;

import java.io.PrintWriter;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.resource.NotSupportedException;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.LocalTransaction;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;

import com.ybxiang.ssh2.ra.api.Logger;
import com.ybxiang.ssh2.ra.api.SSHConnectionRequestInfo;

public class SSHManagedConnection extends SSHPhysicalConnectionOperator implements ManagedConnection {
    public static final Logger log = Logger.getLogger(SSHManagedConnection.class.getName());
    private static final String TRANSACTIONS_NOT_SUPPORTED_ERROR = "Transactions not supported";

    /*****************************[fields used by JCA]*****************************/
    private final SSHConnectionRequestInfo info;
    private PrintWriter m_out;
    private List<ConnectionEventListener> listeners = new LinkedList<ConnectionEventListener>();

    
    /*****************************[methods used by JCA]*****************************/
    public SSHManagedConnection(SSHConnectionRequestInfo info) throws ResourceException{
        super(info);
        //
        log.info("{hashCode("+this.hashCode()+")} > "+"SSHManagedConnection("+info+")");
        this.info = info;
    }

    public Object getConnection(Subject arg0, ConnectionRequestInfo arg1) throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getConnection("+arg0+","+arg1+")");
        return new SSHConnection(this,info);
    }

    public void destroy() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"destroy()");   
        super.close();
    }

    public void cleanup() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"cleanup()");   
    }

    public void associateConnection(Object arg0) throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"associateConnection("+arg0+")");
    }

    public void addConnectionEventListener(ConnectionEventListener arg0) {
        log.info("{hashCode("+this.hashCode()+")} > "+"addConnectionEventListener("+arg0+")");
        listeners.add(arg0);
    }

    public void removeConnectionEventListener(ConnectionEventListener arg0) {
        log.info("{hashCode("+this.hashCode()+")} > "+"removeConnectionEventListener("+arg0+")");
        listeners.remove(arg0);
    }

    public javax.transaction.xa.XAResource getXAResource() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getXAResource()");
        throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED_ERROR);
    }

    public LocalTransaction getLocalTransaction() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getLocalTransaction()");
        throw new NotSupportedException(TRANSACTIONS_NOT_SUPPORTED_ERROR);
    }

    public ManagedConnectionMetaData getMetaData() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getMetaData()");
        return new SSHManagedConnectionMetaData();
    }

    public PrintWriter getLogWriter() throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"getLogWriter()");
        return this.m_out;
    }

    public void setLogWriter(PrintWriter arg0) throws ResourceException {
        log.info("{hashCode("+this.hashCode()+")} > "+"setLogWriter(java.io.PrintWriter "+arg0+")");
        this.m_out = arg0;
    }


    /*****************************[methods used by other classes]*****************************/
    //this method is called by SSHManagedConnectionFactory
    public SSHConnectionRequestInfo getConnectionRequestInfo() {
        log.info("{hashCode("+this.hashCode()+")} > "+"getConnectionRequestInfo()");
        return this.info;
    }
    
    //called by SSHConnection to close related event listeners
    public void closeSSHConnectionEventListener(SSHConnection sshConnection){
      ConnectionEvent event = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
      event.setConnectionHandle(sshConnection);
      //
      Iterator<ConnectionEventListener> list = listeners.iterator();
      while (list.hasNext()) {
          ConnectionEventListener listener = (ConnectionEventListener) list.next();
          listener.connectionClosed(event);
      }            
    }    
}

8.5 JMXBrowser.java

我们新增1个方法,我们打算对不同的 SSH Server访问,看看SSHManagedConnectionFactory.matchManagedConnections 如何处理!


public class JMXBrowser {
    public static final String RMIAdaptor_JNDI_NAME = "jmx/invoker/RMIAdaptor";
    //
    public static final String JNDIViewMBean_ObjectName = "jboss:service=JNDIView";
    public static final String SSHServiceMBean_ObjectName = "com.ybxiang.ssh2:service=SSHService";
   
    public static void main(String[] args) throws Exception {
        //demoJNDIViewMBean();
        //executeSSH2CommandAtHome();
        //
        //executeSSH2CommandAtCompany_ems00();
        executeSSH2CommandAtCompany_ems02();
    }

    /************************[test methods]**************************/
    public static void demoJNDIViewMBean() throws Exception{
        printMBeanInfo(JNDIViewMBean_ObjectName);
        //////
        RMIAdaptor server = getJBossRMIAdaptor();       
        ObjectName objName = new ObjectName(JNDIViewMBean_ObjectName);
        // Invoke the list(boolean) operation
        String[] sig = { "boolean" };
        Object[] opArgs = { Boolean.TRUE };
        Object result = server.invoke(objName, "list", opArgs, sig);
        //
        System.out.println("JNDIView.list(true) output:\n" + result);       
    }
   
    public static void executeSSH2CommandAtHome()throws Exception{
        printMBeanInfo(SSHServiceMBean_ObjectName);
        //
        //Invoke public void executeWithoutWait(SSHConnectionRequestInfo info, String command,String directory);
        RMIAdaptor server = getJBossRMIAdaptor();
        String[] sig = {
                SSHConnectionRequestInfo.class.getName(),
                "java.lang.String",
                "java.lang.String" };
        Object[] opArgs = {
                new SSHConnectionRequestInfo("192.168.1.101","root","ybxiang"),
                "mkdir ybxiangsshJMXtest",
                "/tmp"
                };
        Object result = server.invoke(
                new ObjectName(SSHServiceMBean_ObjectName),
                "executeWithoutWait",
                opArgs,
                sig);
    }
    public static void executeSSH2CommandAtCompany_ems00()throws Exception{
        printMBeanInfo(SSHServiceMBean_ObjectName);
        //
        //Invoke public void executeWithoutWait(SSHConnectionRequestInfo info, String command,String directory);
        RMIAdaptor server = getJBossRMIAdaptor();
        String[] sig = {
                SSHConnectionRequestInfo.class.getName(),
                "java.lang.String",
                "java.lang.String" };
        Object[] opArgs = {
                new SSHConnectionRequestInfo("135.251.246.180","ems00","emsems"),
                "mkdir ybxiangsshJMXtestems00",
                "/home/ems00"
                };
        Object result = server.invoke(
                new ObjectName(SSHServiceMBean_ObjectName),
                "executeWithoutWait",
                opArgs,
                sig);
    }
    public static void executeSSH2CommandAtCompany_ems02()throws Exception{
        printMBeanInfo(SSHServiceMBean_ObjectName);
        //
        //Invoke public void executeWithoutWait(SSHConnectionRequestInfo info, String command,String directory);
        RMIAdaptor server = getJBossRMIAdaptor();
        String[] sig = {
                SSHConnectionRequestInfo.class.getName(),
                "java.lang.String",
                "java.lang.String" };
        Object[] opArgs = {
                new SSHConnectionRequestInfo("135.251.246.180","ems02","emsems"),
                "mkdir ybxiangsshJMXtestems02",
                "/home/ems02"
                };
        Object result = server.invoke(
                new ObjectName(SSHServiceMBean_ObjectName),
                "executeWithoutWait",
                opArgs,
                sig);
    }   
    /************************[static helper methods]**************************/
    public static InitialContext getJBossInitialContext() throws Exception{
        Properties properties = new Properties();
        properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
        properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
        properties.put("java.naming.provider.url","localhost:1099");
        return new InitialContext(properties);          
    }       
    public static RMIAdaptor getJBossRMIAdaptor()throws Exception{
        InitialContext ic = getJBossInitialContext();
        return (RMIAdaptor) ic.lookup(RMIAdaptor_JNDI_NAME);       
    }
}
8.6 EJBClient.java

我们新增1个方法,我们打算对不同的 SSH Server访问,看看SSHManagedConnectionFactory.matchManagedConnections 如何处理!


public class EJBClient {
    public static void main(String args[]) throws Exception {
        Context context = getInitialContext();  
        ISSH2Executor executor = (ISSH2Executor)context.lookup(ISSH2Executor.JNDI_NAME);
       
        //executeAtHome(executor);
        //
        executeAtCompany_ems00(executor);
        executeAtCompany_ems02(executor);
    }
    public static InitialContext getInitialContext() throws Exception{
        Properties properties = new Properties();
        properties.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
        properties.put("java.naming.factory.url.pkgs","=org.jboss.naming:org.jnp.interfaces");
        properties.put("java.naming.provider.url","localhost:1099");
        return new InitialContext(properties);          
    }
   
    public static void executeAtHome(ISSH2Executor executor)throws Exception{
        SSHConnectionRequestInfo info = new SSHConnectionRequestInfo("192.168.1.101","root","ybxiang");
        executor.executeWithoutWait(info, "mkdir ybxiangsshtest", "/tmp");
    }
    public static void executeAtCompany_ems00(ISSH2Executor executor)throws Exception{
        SSHConnectionRequestInfo info = new SSHConnectionRequestInfo("135.251.246.180","ems00","emsems");   
        executor.executeWithoutWait(info, "mkdir ybxiangsshtest", "/home/ems00");
    } 
    public static void executeAtCompany_ems02(ISSH2Executor executor)throws Exception{
        SSHConnectionRequestInfo info = new SSHConnectionRequestInfo("135.251.246.180","ems02","emsems");   
        executor.executeWithoutWait(info, "mkdir ybxiangsshtest", "/home/ems02");
    } 
   
}
9  测试
准备工作:

  • 关闭JBoss
  • 重新把所有API[ybxiang-ssh2-ra-api.jar, ybxiang-ssh2-ejb-api.jar]部署到 D:\java\jboss-5.1.0.GA\server\all\lib
  • 部署所有app[ybxiang-ssh2-ra-app.rar,ybxiang-ssh2-ds.xml,ybxiang-ssh2- jmx-app.sar,ybxiang-ssh2-ejb-app.jar]到D:\java\jboss-5.1.0.GA\server\all \deploy
  • 重新启动JBoss

我们看见相关日志:

【这是JBoss部署ybxiang-ssh2-ra-app.rar打印的日志】
INFO: com.ybxiang.ssh2.ra.app.SSHResourceAdapter>start() > You should initialize something here if necessary.



【这是JBoss部署ybxiang-ssh2-ds.xml打印的日志】
INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > setLogWriter(org.jboss.logging.util.LoggerPluginWriter@2cc581)
INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > createConnectionFactory(org.jboss.resource.connectionmanager.BaseConnectionManager2$ConnectionManagerProxy@15ab2e9)
INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>{hashCode(12421357)} > SSHConnectionFactory constructor...
INFO: [ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=AmsSSHAdapter' to JNDI name 'java:AmsSSHAdapter'
该SSHConnectionFactory被绑定到 java:AmsSSHAdapter
9.1 第一次运行JMXBrowser

9.1.1 其中main方法修改如下:
    public static void main(String[] args) throws Exception {
        executeSSH2CommandAtCompany_ems00();
        //executeSSH2CommandAtCompany_ems02();
    }
9.1.2 JBoss Server日志如下:


[xiang:]由于客户端的调用,连接工厂首次被调用创建连接:
INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>{hashCode(12421357)} > createConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)

[xiang:] 由于连接池内没有任何SSHManagedConnection,所以初始化<min-pool-size>个(这里为3个)。
[xiang:] 注意:construct-No.i >>>表示构造第 i 个SSHManagedConnection
####################构造第 1 个SSHManagedConnection(14051333)

construct-No.1 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > createManagedConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
construct-No.1 >>> INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(14051333)} > SSHPhysicalConnectionOperator() constructor...
construct-No.1 >>> DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(14051333)} > initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
construct-No.1 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14051333)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
construct-No.1 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14051333)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@168997e[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@d66805 handles=0 lastUse=1309846793990 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@355c70 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@147f8aa])
####################构造第 2 个SSHManagedConnection(25591043)
[xiang:] 可以看见,在构造第1个 个SSHManagedConnection(14051333)之后,它立刻被调用以创建SSHConnection,参见“cmd.ems00>>>”标识的日志。
[xiang:] 下面构造第 2 个SSHManagedConnection(25591043)的日志中,混造了 执行命令时的日志,参见“cmd.ems00>>>”标识的日志。
[xiang:] 期间还清理了 第一个 SSHManagedConnection(25591043),参见“cleanup-No.1”标识的日志

cmd.ems00>>>  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14051333)} > getConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)

construct-No.2 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > createManagedConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
construct-No.2 >>> INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(25591043)} > SSHPhysicalConnectionOperator() constructor...
construct-No.2 >>> DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(25591043)} > initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180

cmd.ems00>>> INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(4551109)} > SSHConnection constructor...
cmd.ems00>>> INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(4551109)} > executeWithoutWait(mkdir ybxiangsshJMXtestems00,/home/ems00)
cmd.ems00>>> INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(14051333)} > executeWithoutWait(mkdir ybxiangsshJMXtestems00, /home/ems00)
cmd.ems00>>> DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(14051333)} > executing command cd /home/ems00 && mkdir ybxiangsshJMXtestems00
cmd.ems00>>> INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(4551109)} > close()
cleanup-No.1 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14051333)} > cleanup()

construct-No.2 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(25591043)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
construct-No.2 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(25591043)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@1de81ff[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@1867d03 handles=0 lastUse=1309846794271 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@355c70 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@147f8aa])

####################构造第 3 个SSHManagedConnection(32936290)
construct-No.3 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > createManagedConnection(null,hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
construct-No.3 >>> INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(32936290)} > SSHPhysicalConnectionOperator() constructor...
construct-No.3 >>> DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(32936290)} > initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
construct-No.3 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(32936290)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems00;keyFilePath=null;keyFilePassword=null)
construct-No.3 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(32936290)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@db8258[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@1f69162 handles=0 lastUse=1309846794490 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@355c70 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@147f8aa])

9.2 第2次运行JMXBrowser


注意,我们这次 打算连接到其它 SSH Server(帐号:ems02)。


9.2.1 其中main方法修改如下:
    public static void main(String[] args) throws Exception {
        //executeSSH2CommandAtCompany_ems00();
        executeSSH2CommandAtCompany_ems02();
    }
9.2.2 JBoss Server日志如下:

客户端要求创建连接:

INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>{hashCode(12421357)} > createConnection(hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)


##### Matching.32936290 >>> JBoss发现32936290不匹配【我们假设,连接帐号信息 不相等就不匹配】,直接销毁了该连接。
INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > matchManagedConnections([com.ybxiang.ssh2.ra.app.SSHManagedConnection@1f69162],null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(32936290)} > getConnectionRequestInfo()
ERROR [STDERR] WARN: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > return no matched ManagedConnection (null)
WARN  [JBossManagedConnectionPool] Destroying connection that could not be successfully matched: org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@db8258[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@1f69162 handles=0 lastUse=1309846794490 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@355c70 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@147f8aa]
INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(32936290)} > destroy()
INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(32936290)} > Close SSH connection
##### Matching.25591043 >>> JBoss发现25591043不匹配【我们假设,连接帐号信息 不相等就不匹配】,直接销毁了该连接。
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > matchManagedConnections([com.ybxiang.ssh2.ra.app.SSHManagedConnection@1867d03],null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(25591043)} > getConnectionRequestInfo()
[STDERR] WARN: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > return no matched ManagedConnection (null)
[JBossManagedConnectionPool] Destroying connection that could not be successfully matched: org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@1de81ff[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@1867d03 handles=0 lastUse=1309846794271 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@355c70 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@147f8aa]
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(25591043)} > destroy()
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(25591043)} > Close SSH connection
##### Matching.14051333 >>> JBoss发现14051333不匹配【我们假设,连接帐号信息 不相等就不匹配】,直接销毁了该连接。
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > matchManagedConnections([com.ybxiang.ssh2.ra.app.SSHManagedConnection@d66805],null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14051333)} > getConnectionRequestInfo()
[STDERR] WARN: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > return no matched ManagedConnection (null)
[JBossManagedConnectionPool] Destroying connection that could not be successfully matched: org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@168997e[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@d66805 handles=0 lastUse=1309846794005 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@355c70 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@147f8aa]
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(14051333)} > destroy()
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(14051333)} > Close SSH connection
现在连接池里面一个 SSHManagedConnection都没有了。

创建新的连接:28137824
construct-No.4 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > createManagedConnection(null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
construct-No.4 >>> INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(28137824)} > SSHPhysicalConnectionOperator() constructor...
construct-No.4 >>> DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(28137824)} > initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
construct-No.4 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(28137824)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
construct-No.4 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(28137824)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@c83a0d[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@1ad5960 handles=0 lastUse=1309846813629 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@355c70 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@147f8aa])

SSHManagedConnection创建SSHConnection,执行business methods,完毕之后,被回收!
cmd.ems02 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(28137824)} > getConnection(null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
cmd.ems02 >>> INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(22765881)} > SSHConnection constructor...
cmd.ems02 >>> INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(22765881)} > executeWithoutWait(mkdir ybxiangsshJMXtestems02,/home/ems02)
cmd.ems02 >>> INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(28137824)} > executeWithoutWait(mkdir ybxiangsshJMXtestems02, /home/ems02)
cmd.ems02 >>> DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(28137824)} > executing command cd /home/ems02 && mkdir ybxiangsshJMXtestems02
cmd.ems02 >>> INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(22765881)} > close()
cmd.ems02 >>> INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(28137824)} > cleanup()
关于上面直接销毁 不匹配的ManagedConnection的原因,我们小小总结一下:

第一次运行JMXBrowser时创建的3个SSHManagedConnection都不符合!全部关闭!

这种做法在下面的场合非常有用:
  • 某资源的帐号密码被修改了(登陆名没有变化),如果旧的SSHManagedConnection不被销毁,那么JBoss的连接池内可能就会堆积很多没有用的SSHManagedConnection。
时间长了以后,如果无用的SSHManagedConnection个数达到了<max-pool-size>,这个ResourceAdapter就彻底没用了!必须重新启动。
  • 同一个资源,由于时间长了,密码忘记了,需要重新建立帐号。和(a)情况相同。

  1. 如果帐号信息由client端提供(资源 的帐号/密码 多变的情况下,比如定期的密码修改),那么数据源文件 xxx-ds.xml 中最好不要包含帐号信息。就像这个SSH Resource Adapter一样。
  2. 如 果帐号信息基本不会修改,比如电信机房里面的数据库帐号,那么我们就把帐号信息通过 数据源文件 xxx-ds.xml 进行配置。可以用<config-property name="xxx" type="xxx">xxx</config-property>配置

配置数据库的datasource时,同一个DB,有多个帐号,不同的帐号要对应不同的xxx-ds.xml文件!
9.3 第3次运行JMXBrowser


9.3.1 这次,我们依旧执行executeSSH2CommandAtCompany_ems02();

public class JMXBrowser {
    public static void main(String[] args) throws Exception {
        //executeSSH2CommandAtCompany_ems00();
        executeSSH2CommandAtCompany_ems02();
    }
    ...
}
9.3.2 JBoss server日志:


##### Matching.28137824 >>> 匹配成功,直接使用上面创建好的 28137824 连接

[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>{hashCode(12421357)} > createConnection(hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > matchManagedConnections([com.ybxiang.ssh2.ra.app.SSHManagedConnection@1ad5960],null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(28137824)} > getConnectionRequestInfo()
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(19226043)} > return found SSHManagedConnection with hashCode(28137824)


最后,资源被回收:
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(28137824)} > getConnection(null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(399759)} > SSHConnection constructor...
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(399759)} > executeWithoutWait(mkdir ybxiangsshJMXtestems02,/home/ems02)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(28137824)} > executeWithoutWait(mkdir ybxiangsshJMXtestems02, /home/ems02)
[STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(28137824)} > executing command cd /home/ems02 && mkdir ybxiangsshJMXtestems02
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnection>{hashCode(399759)} > close()
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(28137824)} > cleanup()
9.3.3 长时间超时

销毁1
15:06:03,165 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(11557094)} > destroy()
15:06:03,165 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(11557094)} > Close SSH connection

销毁2
15:06:03,165 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(8884544)} > destroy()
15:06:03,165 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(8884544)} > Close SSH connection

销毁3

15:06:03,165 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(29242398)} > destroy()
15:06:03,165 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(29242398)} > Close SSH connection


构造1
15:06:03,196 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(13714606)} > createManagedConnection(null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
15:06:03,196 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(4788456)} > SSHPhysicalConnectionOperator() constructor...
15:06:03,196 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(4788456)} > initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
15:06:03,446 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(4788456)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
15:06:03,446 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(4788456)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@10276a4[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@4910e8 handles=0 lastUse=1309849563446 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@272321 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@1737cd8])

构造2
15:06:03,446 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(13714606)} > createManagedConnection(null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
15:06:03,446 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(12268168)} > SSHPhysicalConnectionOperator() constructor...
15:06:03,446 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(12268168)} > initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
15:06:03,774 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(12268168)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
15:06:03,774 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(12268168)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@8938a8[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@bb3288 handles=0 lastUse=1309849563774 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@272321 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@1737cd8])

构造3

15:06:03,774 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(13714606)} > createManagedConnection(null,hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
15:06:03,774 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(15119471)} > SSHPhysicalConnectionOperator() constructor...
15:06:03,774 INFO  [STDOUT] DEBUG: com.ybxiang.ssh2.ra.app.SSHPhysicalConnectionOperator>{hashCode(15119471)} > initConnection():Creating a SSH Connection with Public Key : null, HostName:135.251.246.180
15:06:04,118 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(15119471)} > SSHManagedConnection(hostName=135.251.246.180;userName=ems02;keyFilePath=null;keyFilePassword=null)
15:06:04,118 INFO  [STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnection>{hashCode(15119471)} > addConnectionEventListener(org.jboss.resource.connectionmanager.NoTxConnectionManager$NoTxConnectionEventListener@123ba32[state=NORMAL mc=com.ybxiang.ssh2.ra.app.SSHManagedConnection@e6b46f handles=0 lastUse=1309849564118 permit=false trackByTx=false mcp=org.jboss.resource.connectionmanager.JBossManagedConnectionPool$OnePool@272321 context=org.jboss.resource.connectionmanager.InternalManagedConnectionPool@1737cd8])

...
好了,现在基本满足我的需求了。
;)
10. 通过ybxiang-ssh2-ds.xml向SSHManagedConnectionFactory传递参数【可以是各种帐号,size】


注意,

xxx-ds.xml 是JBoss自定义的规范:"http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd"

不像ra.xml一样,是标准的:http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd
10.1 ybxiang-ssh2-ds.xml

修改如下


<!DOCTYPE connection-factories PUBLIC
          "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
          "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
<connection-factories>
    <no-tx-connection-factory>
        <jndi-name>AmsSSHAdapter</jndi-name>
        <rar-name>ybxiang-ssh2-ra-app.rar</rar-name>
        <connection-definition>com.ybxiang.ssh2.ra.api.ISSHConnectionFactory</connection-definition>       
        <config-property name="UserName" type="java.lang.String">XiangYingBing</config-property>
        <config-property name="FirstName" type="java.lang.String">xiang</config-property>
        <config-property name="LastName" type="java.lang.String">yingbing</config-property>

        <min-pool-size>3</min-pool-size>
        <max-pool-size>4</max-pool-size>
        <blocking-timeout-millis>5000</blocking-timeout-millis>
        <idle-timeout-minutes>10</idle-timeout-minutes>
        <!-- <application-managed-security/> -->
    </no-tx-connection-factory>
</connection-factories>
10.2 SSHManagedConnectionFactory.java

在这个class中加入下面的代码:

    //parameters can ge passed into ManagedConnectionFactory by ybxiang-ssh2-ds.xml
    private String user;
    private String firstName;
    private String lastName;

    public void setUserName(String userName){
        log.info("{hashCode("+this.hashCode()+")} > "+"userName:"+userName);
        this.user = userName;
    }
    public void setFirstName(String firstName){
        log.info("{hashCode("+this.hashCode()+")} > "+"firstName:"+firstName);
        this.firstName = firstName;
    }
    public void setLastName(String lastName){
        log.info("{hashCode("+this.hashCode()+")} > "+"lastName:"+lastName);
        this.lastName = lastName;
    }



注意,只要setter方法中,set后面的名字和xml中的名字匹配就可以了!
10.3 重新部署ybxiang-ssh2-ra-app.rar和ybxiang-ssh2-ds.xml

日志:
[ConnectionFactoryBindingService] Unbound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=AmsSSHAdapter' from JNDI name 'java:AmsSSHAdapter'
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(14100708)} > userName:XiangYingBing
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(14100708)} > firstName:xiang
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(14100708)} > lastName:yingbing

[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(14100708)} > setLogWriter(org.jboss.logging.util.LoggerPluginWriter@c116a8)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(14100708)} > createConnectionFactory(...)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>{hashCode(22219402)} > SSHConnectionFactory constructor...
[ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=AmsSSHAdapter' to JNDI name 'java:AmsSSHAdapter'
10.4 还可以通过ra.xml传递参数到ManagedConnectionFactory里面!

如下:
(a) ra.xml
<?xml version="1.0" encoding="UTF-8"?>
<connector version="1.5"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/connector_1_5.xsd">
    <display-name>SSH2 Adapter</display-name>
    <vendor-name>Xiang YingBing</vendor-name>
    <eis-type>SSH2 System</eis-type>
    <resourceadapter-version>1.0</resourceadapter-version>
    <license>
        <description>LGPL</description>
        <license-required>false</license-required>
    </license>
    <resourceadapter>
        <resourceadapter-class>com.ybxiang.ssh2.ra.app.SSHResourceAdapter</resourceadapter-class>
        <outbound-resourceadapter>
            <connection-definition>
                <managedconnectionfactory-class>com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory</managedconnectionfactory-class>

                <config-property>
                    <config-property-name>ParameterPassedByResourceAdapterXML</config-property-name>
                    <config-property-type>java.lang.String</config-property-type>
                    <config-property-value>this is a parameter passed by ra.xml</config-property-value>
                </config-property>

...


(b) SSHManagedConnectionFactory
public class SSHManagedConnectionFactory implements ManagedConnectionFactory {
....
    //parameters can ge passed into ManagedConnectionFactory by ra.xml
    private String parameterPassedByResourceAdapterXML;
    public void setParameterPassedByResourceAdapterXML(String value){
        log.info("{hashCode("+this.hashCode()+")} > "+"parameterPassedByResourceAdapterXML:"+value);
        parameterPassedByResourceAdapterXML = value;
    }


(c)重新部署后的日志:

[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(26890590)} > parameterPassedByResourceAdapterXML:this is a parameter passed by ra.xml
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(26890590)} > userName:XiangYingBing
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(26890590)} > firstName:xiang
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(26890590)} > lastName:yingbing
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(26890590)} > setLogWriter(org.jboss.logging.util.LoggerPluginWriter@6714db)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHManagedConnectionFactory>{hashCode(26890590)} > createConnectionFactory(org.jboss.resource.connectionmanager.BaseConnectionManager2$ConnectionManagerProxy@1eb9cc2)
[STDOUT] INFO: com.ybxiang.ssh2.ra.app.SSHConnectionFactory>{hashCode(32730874)} > SSHConnectionFactory constructor...
[ConnectionFactoryBindingService] Bound ConnectionManager 'jboss.jca:service=ConnectionFactoryBinding,name=AmsSSHAdapter' to JNDI name 'java:AmsSSHAdapter'


我们完全可以把帐号信息通过 ra.xml(标准)和xxx-ds.xml(jboss定义dtd) 传递到ManagedConnectionFactory里面!!!这样客户端就不用传递帐号信息了!跟配置数据源一样。
11. 总结

(1) business连接(这里为SSHConnection)在被使用完毕之后,应该清理它在对应的ManagedConnection中,和它自己相关的 ConnectionEventListener。比如,SSHConnection调用 SSHManagedConnection.closeEventListener(this),关闭的时候,发送Event:
    public void closeEventListener(SSHConnection sshConnection){
      ConnectionEvent event = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
      event.setConnectionHandle(sshConnection);
      //
      Iterator<ConnectionEventListener> list = listeners.iterator();
      while (list.hasNext()) {
          ConnectionEventListener listener = (ConnectionEventListener) list.next();
          listener.connectionClosed(event);
      }           
    }   
(2) JBoss收到Event之后,检测相关SSHManagedConnection中是否还有和这个business连接(这里为SSHConnection)相关的ConnectionEventListener:
  • 如果没有,那么JBoss销毁该SSHConnection instance,并执行SSHManagedConnection.cleanup(),最后把该SSHManagedConnection放回连接池。
  • 如果还有,那么JBoss就无法销毁该SSHConnection instance,无法把该SSHManagedConnection放回连接池。该SSHManagedConnection就永远被占用着!
(3) 因为business连接(这里为SSHConnection) 在使用完毕后,需要被销毁,所以对底层的资源的真正的连接(这里为ch.ethz.ssh2.Connection)不能在business连接(这里为 SSHConnection) 中定义!必须放到SSHManagedConnection中!

(4) 在SSHManagedConnection中,只有在JBoss停止的时候,才关闭对底层的资源的真正的连接(这里为 ch.ethz.ssh2.Connection),所以关闭动作应该放在 SSHManagedConnection.destroy()中执行!

This text field can contain stylized text and graphics. To cycle through all formatting options, use the keyboard shortcut Control + Shift + T to place focus on the toolbar and navigate between option heading names.

Common formatting keyboard shortcuts:

  • Control Shift B sets text to bold
  • Control Shift I sets text to italic
  • Control Shift U underlines text
  • Control Shift [ aligns text left
  • Control Shift | centers text
  • Control Shift ] aligns text right
  • Control Shift L adds an HTML link
  • To exit this text editor use the keyboard shortcut Control + Shift + ESC.
body

You have left the Rich Text Editor.


Home  Header
www.jdetail.com