001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: welker $' 006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 007 * '$Revision: 24234 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.ssh; 031 032import java.util.ArrayList; 033import java.util.Hashtable; 034import java.util.List; 035 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038 039/** 040 * This class provides a factory to give you an object implementing the 041 * ExecInterface. It provides either an LocalExec object or an RemoteExec object 042 * based on the target you provide. RemoteExec object can either be an instance of 043 * SshExec or GsiSshExec based on whether the remote host supports grid authentication 044 * or not. The reference to a target is with "user@host:port". If port is not given, 045 * first an attempt is made to connect using gssapi at port 2222. If that fails, 046 * default to ssh at port 22. 047 * 048 * <p> 049 * 050 * @author Norbert Podhorszki 051 */ 052 053public class ExecFactory { 054 055 private static final String GSSAPI = "gssapi"; 056 private static final String SSH ="SSH"; 057 private ExecFactory() { 058 } 059 060 private static final Log log = LogFactory.getLog(ExecFactory.class.getName()); 061 private static final boolean isDebugging = log.isDebugEnabled(); 062 063 private static Hashtable<String,String> authenticationMethod = new Hashtable<String,String>(); 064 065 /** 066 * Helper method to provide the target as a complete string. This method 067 * calls the other one after processing the input string. 068 * @throws ExecException 069 */ 070 public static ExecInterface getExecObject(String target) throws ExecException 071 { 072 // get USER 073 String user, host; 074 int port = -1; 075 076 int atPos = target.indexOf('@'); 077 if (atPos >= 0) 078 user = target.substring(0, target.indexOf('@')); 079 else 080 user = System.getProperty("user.name"); 081 082 // get the HOST and PORT 083 int colonPos = target.indexOf(':'); 084 if (colonPos >= 0 && colonPos > atPos) { 085 host = target.substring(atPos + 1, colonPos); 086 String portStr = target.substring(colonPos + 1); 087 try { 088 port = Integer.parseInt(portStr); 089 } catch (java.lang.NumberFormatException ex) { 090 log.error("The port should be a number or omitted in "+ target); 091 } 092 } else 093 host = target.substring(atPos + 1); 094 return ExecFactory.getExecObject(user, host, port); 095 } 096 097 @SuppressWarnings("unchecked") 098 public static ExecInterface getExecObject(String user, String host) 099 throws ExecException 100 { 101 return getExecObject(user, host, -1); 102 } 103 104 /** 105 * Return an object with ExecInterface, based on the specified target. It 106 * will be a LocalExec if host is null, empty or equals 'local', otherwise 107 * it will be an SshExec or GsiSshExec object based on the supported 108 * authentication mechanism. Grid authentication is given priority over 109 * ssh authentication. If no valid port is provided, first tries 110 * grid authentication at port 2222, if that fails defaults to ssh 111 * at port 22. The check for available authentications method is done only 112 * once per remote host and cached for later use. 113 * @throws ExecException 114 */ 115 @SuppressWarnings("unchecked") 116 public static ExecInterface getExecObject(String user, String host, int port) 117 throws ExecException 118 { 119 120 if (host == null || host.trim().equals("") 121 || host.equals("localhost") || host.equals("local") ) { 122 // local execution 123 if (isDebugging) { 124 log.debug("Provide LocalExec"); 125 } 126 return new LocalExec(); 127 } else { 128 return getRemoteExec(user, host, port); 129 } 130 } 131 132 @SuppressWarnings("unchecked") 133 private static ExecInterface getExecByEarlierMethod(String user, String host, int port) 134 throws ExecException 135 { 136 137 String am = authenticationMethod.get(user + "@" + host + ":" + port); 138 if (GSSAPI.equals(am)) { 139 log.info("Providing new GsiSshExec for " + user + "@" + host + ":" + port); 140 return new GsiSshExec(user,host,port); 141 } else if (SSH.equals(am)) { 142 log.info("Providing new SshExec for " + user + "@" + host + ":" + port); 143 return new SshExec(user,host,port); 144 } 145 return null; 146 } 147 148 @SuppressWarnings("unchecked") 149 private static ExecInterface getRemoteExec(String user, String host, int port) 150 throws ExecException 151 { 152 153 //REMOTE HOST 154 //Check if the server support grid authentication 155 ExecInterface execObj = null; 156 String target = user + "@" + host + ":" + port; 157 String cert = System.getProperty("X509_USER_CERT"); 158 String proxy = System.getProperty("X509_USER_PROXY"); 159 boolean trygsi = (cert != null || proxy != null); 160 161 // check if we had a factory call for this target before 162 if (port>0) { 163 execObj = getExecByEarlierMethod (user, host, port); 164 } else { 165 if (trygsi) 166 execObj = getExecByEarlierMethod (user, host, 2222); 167 if (execObj == null) 168 execObj = getExecByEarlierMethod (user, host, 22); 169 } 170 if (execObj != null) 171 return execObj; 172 173 // try the GSI options first 174 if (trygsi) { 175 int testport = port; 176 if (port > 0) { 177 execObj = getGsisshExec(user, host, port); 178 } else { 179 // Try port 2222 180 testport = 2222; 181 execObj = getGsisshExec(user, host, testport); 182 if (execObj == null) { 183 // Try port 22 184 testport = 22; 185 execObj = getGsisshExec(user, host, testport); 186 } 187 } 188 189 if (execObj != null) { 190 // we have a GSI-SSH server 191 authenticationMethod.put(user + "@" + host + ":" + testport, GSSAPI); 192 log.info("Providing new GsiSshExec for " + user + "@" + host + ":" + testport); 193 return execObj; 194 } 195 } // end if (trygsi) 196 197 // last chance is a traditional SSH connection 198 if (port>0) { 199 log.info("Providing new SshExec for " + user + "@" + host + ":" + port); 200 execObj = new SshExec(user, host, port); 201 authenticationMethod.put(user + "@" + host + ":" + port, SSH); 202 } else { 203 log.info("Providing new SshExec for " + user + "@" + host + ":22"); 204 execObj = new SshExec(user, host, 22); 205 authenticationMethod.put(user + "@" + host + ":22", SSH); 206 } 207 return execObj; 208 } 209 210 private static GsiSshExec getGsisshExec(String user, String host, int port) { 211 log.debug("Try GSI server at " + user + "@" + host + ":"+ port); 212 GsiSshExec result = null; 213 GsiSshExec temp = new GsiSshExec(user,host,port); 214 List availableMethods = new ArrayList(); 215 try { 216 availableMethods = temp.getAvailableAuthMethods(); 217 log.info("Available authentication mechanism for "+ user + "@" + host + ":"+ port +" -- " + availableMethods ); 218 } catch(SshException e) { 219 temp.closeConnection(); 220 log.warn("Unable to retrieve available authentication mechanisms."); 221 } 222 if(availableMethods==null){ 223 log.warn("Unable to retrieve available authentication mechanisms."); 224 temp.closeConnection(); 225 } 226 else if (availableMethods.contains(GSSAPI)) { 227 result = temp; 228 } else { 229 temp.closeConnection(); 230 } 231 return result; 232 } 233 234 235 236}