1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package org.apache.jmeter.protocol.ftp.sampler; 20 21 import java.io.ByteArrayInputStream; 22 import java.io.ByteArrayOutputStream; 23 import java.io.File; 24 import java.io.FileInputStream; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.OutputStream; 29 import java.net.MalformedURLException; 30 import java.net.URL; 31 32 import org.apache.commons.io.IOUtils; 33 import org.apache.commons.io.output.NullOutputStream; 34 import org.apache.commons.io.output.TeeOutputStream; 35 import org.apache.commons.lang.text.StrBuilder; 36 import org.apache.commons.net.ftp.FTP; 37 import org.apache.commons.net.ftp.FTPClient; 38 import org.apache.commons.net.ftp.FTPReply; 39 import org.apache.jmeter.config.ConfigTestElement; 40 import org.apache.jmeter.samplers.AbstractSampler; 41 import org.apache.jmeter.samplers.Entry; 42 import org.apache.jmeter.samplers.Interruptible; 43 import org.apache.jmeter.samplers.SampleResult; 44 import org.apache.jorphan.logging.LoggingManager; 45 import org.apache.log.Logger; 46 47 /** 48 * A sampler which understands FTP file requests. 49 * 50 */ 51 public class FTPSampler extends AbstractSampler implements Interruptible { 52 private static final Logger log = LoggingManager.getLoggerForClass(); 53 54 public final static String SERVER = "FTPSampler.server"; // $NON-NLS-1$ 55 56 public final static String PORT = "FTPSampler.port"; // $NON-NLS-1$ 57 58 // N.B. Originally there was only one filename, and only get(RETR) was supported 59 // To maintain backwards compatibility, the property name needs to remain the same 60 public final static String REMOTE_FILENAME = "FTPSampler.filename"; // $NON-NLS-1$ 61 62 public final static String LOCAL_FILENAME = "FTPSampler.localfilename"; // $NON-NLS-1$ 63 64 public final static String INPUT_DATA = "FTPSampler.inputdata"; // $NON-NLS-1$ 65 66 // Use binary mode file transfer? 67 public final static String BINARY_MODE = "FTPSampler.binarymode"; // $NON-NLS-1$ 68 69 // Are we uploading? 70 public final static String UPLOAD_FILE = "FTPSampler.upload"; // $NON-NLS-1$ 71 72 // Should the file data be saved in the response? 73 public final static String SAVE_RESPONSE = "FTPSampler.saveresponse"; // $NON-NLS-1$ 74 75 private volatile FTPClient savedClient; 76 77 public FTPSampler() { 78 } 79 80 public String getUsername() { 81 return getPropertyAsString(ConfigTestElement.USERNAME); 82 } 83 84 public String getPassword() { 85 return getPropertyAsString(ConfigTestElement.PASSWORD); 86 } 87 88 public void setServer(String newServer) { 89 this.setProperty(SERVER, newServer); 90 } 91 92 public String getServer() { 93 return getPropertyAsString(SERVER); 94 } 95 96 public void setPort(String newPort) { 97 this.setProperty(PORT, newPort, ""); // $NON-NLS-1$ 98 } 99 100 public String getPort() { 101 return getPropertyAsString(PORT, ""); // $NON-NLS-1$ 102 } 103 104 public int getPortAsInt() { 105 return getPropertyAsInt(PORT, 0); 106 } 107 108 public String getRemoteFilename() { 109 return getPropertyAsString(REMOTE_FILENAME); 110 } 111 112 public String getLocalFilename() { 113 return getPropertyAsString(LOCAL_FILENAME); 114 } 115 116 private String getLocalFileContents() { 117 return getPropertyAsString(INPUT_DATA); 118 } 119 120 public boolean isBinaryMode(){ 121 return getPropertyAsBoolean(BINARY_MODE,false); 122 } 123 124 public boolean isSaveResponse(){ 125 return getPropertyAsBoolean(SAVE_RESPONSE,false); 126 } 127 128 public boolean isUpload(){ 129 return getPropertyAsBoolean(UPLOAD_FILE,false); 130 } 131 132 133 /** 134 * Returns a formatted string label describing this sampler Example output: 135 * ftp://ftp.nowhere.com/pub/README.txt 136 * 137 * @return a formatted string label describing this sampler 138 */ 139 public String getLabel() { 140 StrBuilder sb = new StrBuilder(); 141 sb.setNullText("null");// $NON-NLS-1$ 142 sb.append("ftp://");// $NON-NLS-1$ 143 sb.append(getServer()); 144 String port = getPort(); 145 if (port.length() > 0){ 146 sb.append(':'); 147 sb.append(port); 148 } 149 sb.append("/");// $NON-NLS-1$ 150 sb.append(getRemoteFilename()); 151 sb.append(isBinaryMode() ? " (Binary) " : " (Ascii) ");// $NON-NLS-1$ $NON-NLS-2$ 152 sb.append(isUpload() ? " <- " : " -> "); // $NON-NLS-1$ $NON-NLS-2$ 153 sb.append(getLocalFilename()); 154 return sb.toString(); 155 } 156 157 public SampleResult sample(Entry e) { 158 SampleResult res = new SampleResult(); 159 res.setSuccessful(false); // Assume failure 160 String remote = getRemoteFilename(); 161 String local = getLocalFilename(); 162 boolean binaryTransfer = isBinaryMode(); 163 res.setSampleLabel(getName()); 164 final String label = getLabel(); 165 res.setSamplerData(label); 166 try { 167 res.setURL(new URL(label)); 168 } catch (MalformedURLException e1) { 169 log.warn("Cannot set URL: "+e1.getLocalizedMessage()); 170 } 171 InputStream input = null; 172 OutputStream output = null; 173 174 res.sampleStart(); 175 FTPClient ftp = new FTPClient(); 176 try { 177 savedClient = ftp; 178 final int port = getPortAsInt(); 179 if (port > 0){ 180 ftp.connect(getServer(),port); 181 } else { 182 ftp.connect(getServer()); 183 } 184 res.latencyEnd(); 185 int reply = ftp.getReplyCode(); 186 if (FTPReply.isPositiveCompletion(reply)) 187 { 188 if (ftp.login( getUsername(), getPassword())){ 189 if (binaryTransfer) { 190 ftp.setFileType(FTP.BINARY_FILE_TYPE); 191 } 192 ftp.enterLocalPassiveMode();// should probably come from the setup dialog 193 boolean ftpOK=false; 194 if (isUpload()) { 195 String contents=getLocalFileContents(); 196 if (contents.length() > 0){ 197 byte bytes[] = contents.getBytes();// TODO this assumes local encoding 198 input = new ByteArrayInputStream(bytes); 199 res.setBytes(bytes.length); 200 } else { 201 File infile = new File(local); 202 res.setBytes((int)infile.length()); 203 input = new FileInputStream(infile); 204 } 205 ftpOK = ftp.storeFile(remote, input); 206 } else { 207 final boolean saveResponse = isSaveResponse(); 208 ByteArrayOutputStream baos=null; // No need to close this 209 OutputStream target=null; // No need to close this 210 if (saveResponse){ 211 baos = new ByteArrayOutputStream(); 212 target=baos; 213 } 214 if (local.length()>0){ 215 output=new FileOutputStream(local); 216 if (target==null) { 217 target=output; 218 } else { 219 target = new TeeOutputStream(output,baos); 220 } 221 } 222 if (target == null){ 223 target=new NullOutputStream(); 224 } 225 input = ftp.retrieveFileStream(remote); 226 if (input == null){// Could not access file or other error 227 res.setResponseCode(Integer.toString(ftp.getReplyCode())); 228 res.setResponseMessage(ftp.getReplyString()); 229 } else { 230 long bytes = IOUtils.copy(input,target); 231 ftpOK = bytes > 0; 232 if (saveResponse && baos != null){ 233 res.setResponseData(baos.toByteArray()); 234 if (!binaryTransfer) { 235 res.setDataType(SampleResult.TEXT); 236 } 237 } else { 238 res.setBytes((int) bytes); 239 } 240 } 241 } 242 243 if (ftpOK) { 244 res.setResponseCodeOK(); 245 res.setResponseMessageOK(); 246 res.setSuccessful(true); 247 } else { 248 res.setResponseCode(Integer.toString(ftp.getReplyCode())); 249 res.setResponseMessage(ftp.getReplyString()); 250 } 251 } else { 252 res.setResponseCode(Integer.toString(ftp.getReplyCode())); 253 res.setResponseMessage(ftp.getReplyString()); 254 } 255 } else { 256 res.setResponseCode("501"); // TODO 257 res.setResponseMessage("Could not connect"); 258 //res.setResponseCode(Integer.toString(ftp.getReplyCode())); 259 res.setResponseMessage(ftp.getReplyString()); 260 } 261 } catch (IOException ex) { 262 res.setResponseCode("000"); // TODO 263 res.setResponseMessage(ex.toString()); 264 } finally { 265 savedClient = null; 266 if (ftp.isConnected()) { 267 try { 268 ftp.logout(); 269 } catch (IOException ignored) { 270 } 271 try { 272 ftp.disconnect(); 273 } catch (IOException ignored) { 274 } 275 } 276 IOUtils.closeQuietly(input); 277 IOUtils.closeQuietly(output); 278 } 279 280 res.sampleEnd(); 281 return res; 282 } 283 284 /** {@inheritDoc} */ 285 public boolean interrupt() { 286 FTPClient client = savedClient; 287 if (client != null) { 288 savedClient = null; 289 try { 290 client.abort(); 291 client.disconnect(); 292 } catch (IOException ignored) { 293 } 294 } 295 return client != null; 296 } 297 }