001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.protocol.tcp.sampler;
020:
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileNotFoundException;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.io.OutputStream;
027: import java.net.Socket;
028: import java.net.UnknownHostException;
029: import java.util.HashMap;
030: import java.util.Map;
031: import java.util.Properties;
032:
033: import org.apache.jmeter.config.ConfigTestElement;
034: import org.apache.jmeter.util.JMeterUtils;
035: import org.apache.jmeter.samplers.AbstractSampler;
036: import org.apache.jmeter.samplers.Entry;
037: import org.apache.jmeter.samplers.SampleResult;
038: import org.apache.jmeter.testelement.ThreadListener;
039: import org.apache.jorphan.logging.LoggingManager;
040: import org.apache.jorphan.util.JOrphanUtils;
041: import org.apache.log.Logger;
042:
043: /**
044: * A sampler which understands Tcp requests.
045: *
046: */
047: public class TCPSampler extends AbstractSampler implements
048: ThreadListener {
049: private static final Logger log = LoggingManager
050: .getLoggerForClass();
051:
052: public final static String SERVER = "TCPSampler.server"; //$NON-NLS-1$
053:
054: public final static String PORT = "TCPSampler.port"; //$NON-NLS-1$
055:
056: public final static String FILENAME = "TCPSampler.filename"; //$NON-NLS-1$
057:
058: public final static String CLASSNAME = "TCPSampler.classname";//$NON-NLS-1$
059:
060: public final static String NODELAY = "TCPSampler.nodelay"; //$NON-NLS-1$
061:
062: public final static String TIMEOUT = "TCPSampler.timeout"; //$NON-NLS-1$
063:
064: public final static String REQUEST = "TCPSampler.request"; //$NON-NLS-1$
065:
066: public final static String RE_USE_CONNECTION = "TCPSampler.reUseConnection"; //$NON-NLS-1$
067:
068: private final static String TCPKEY = "TCP"; //$NON-NLS-1$ key for HashMap
069:
070: private final static String ERRKEY = "ERR"; //$NON-NLS-1$ key for HashMap
071:
072: // If set, this is the regex that is used to extract the status from the
073: // response
074: // NOT implemented yet private final static String STATUS_REGEX =
075: // JMeterUtils.getPropDefault("tcp.status.regex","");
076:
077: // Otherwise, the response is scanned for these strings
078: private final static String STATUS_PREFIX = JMeterUtils
079: .getPropDefault("tcp.status.prefix", "");
080:
081: private final static String STATUS_SUFFIX = JMeterUtils
082: .getPropDefault("tcp.status.suffix", "");
083:
084: private final static String STATUS_PROPERTIES = JMeterUtils
085: .getPropDefault("tcp.status.properties", "");
086:
087: private final static Properties statusProps = new Properties();
088:
089: private static boolean haveStatusProps = false;
090:
091: static {
092: log.debug("Protocol Handler name=" + getClassname());
093: log.debug("Status prefix=" + STATUS_PREFIX);
094: log.debug("Status suffix=" + STATUS_SUFFIX);
095: log.debug("Status properties=" + STATUS_PROPERTIES);
096: if (STATUS_PROPERTIES.length() > 0) {
097: File f = new File(STATUS_PROPERTIES);
098: FileInputStream fis = null;
099: try {
100: fis = new FileInputStream(f);
101: statusProps.load(fis);
102: log.debug("Successfully loaded properties");
103: haveStatusProps = true;
104: } catch (FileNotFoundException e) {
105: log.debug("Property file not found");
106: } catch (IOException e) {
107: log.debug("Property file error " + e.toString());
108: } finally {
109: JOrphanUtils.closeQuietly(fis);
110: }
111: }
112: }
113:
114: /** the cache of TCP Connections */
115: private static ThreadLocal tp = new ThreadLocal() {
116: protected Object initialValue() {
117: return new HashMap();
118: }
119: };
120:
121: private transient TCPClient protocolHandler;
122:
123: public TCPSampler() {
124: log.debug("Created " + this );
125: protocolHandler = getProtocol();
126: log.debug("Using Protocol Handler: "
127: + protocolHandler.getClass().getName());
128: }
129:
130: private String getError() {
131: Map cp = (Map) tp.get();
132: return (String) cp.get(ERRKEY);
133: }
134:
135: private Socket getSocket() {
136: Map cp = (Map) tp.get();
137: Socket con = null;
138: if (isReUseConnection()) {
139: con = (Socket) cp.get(TCPKEY);
140: if (con != null) {
141: log.debug(this + " Reusing connection " + con); //$NON-NLS-1$
142: return con;
143: }
144: }
145:
146: // Not in cache, so create new one and cache it
147: try {
148: con = new Socket(getServer(), getPort());
149: con.setSoTimeout(getTimeout());
150: con.setTcpNoDelay(getNoDelay());
151:
152: log
153: .debug(this
154: + " Timeout " + getTimeout() + " NoDelay " + getNoDelay()); //$NON-NLS-1$
155: log.debug("Created new connection " + con); //$NON-NLS-1$
156: cp.put(TCPKEY, con);
157: } catch (UnknownHostException e) {
158: log.warn("Unknown host for " + getLabel(), e);//$NON-NLS-1$
159: cp.put(ERRKEY, e.toString());
160: } catch (IOException e) {
161: log.warn("Could not create socket for " + getLabel(), e); //$NON-NLS-1$
162: cp.put(ERRKEY, e.toString());
163: }
164: return con;
165: }
166:
167: public String getUsername() {
168: return getPropertyAsString(ConfigTestElement.USERNAME);
169: }
170:
171: public String getPassword() {
172: return getPropertyAsString(ConfigTestElement.PASSWORD);
173: }
174:
175: public void setServer(String newServer) {
176: this .setProperty(SERVER, newServer);
177: }
178:
179: public String getServer() {
180: return getPropertyAsString(SERVER);
181: }
182:
183: public void setReUseConnection(String newServer) {
184: this .setProperty(RE_USE_CONNECTION, newServer);
185: }
186:
187: public boolean isReUseConnection() {
188: return getPropertyAsBoolean(RE_USE_CONNECTION);
189: }
190:
191: public void setPort(String newFilename) {
192: this .setProperty(PORT, newFilename);
193: }
194:
195: public int getPort() {
196: return getPropertyAsInt(PORT);
197: }
198:
199: public void setFilename(String newFilename) {
200: this .setProperty(FILENAME, newFilename);
201: }
202:
203: public String getFilename() {
204: return getPropertyAsString(FILENAME);
205: }
206:
207: public void setRequestData(String newRequestData) {
208: this .setProperty(REQUEST, newRequestData);
209: }
210:
211: public String getRequestData() {
212: return getPropertyAsString(REQUEST);
213: }
214:
215: public void setTimeout(String newTimeout) {
216: this .setProperty(FILENAME, newTimeout);
217: }
218:
219: public int getTimeout() {
220: return getPropertyAsInt(TIMEOUT);
221: }
222:
223: public void setNoDelay(String newNoDelay) {
224: this .setProperty(NODELAY, newNoDelay);
225: }
226:
227: public boolean getNoDelay() {
228: return getPropertyAsBoolean(NODELAY);
229: }
230:
231: /**
232: * Returns a formatted string label describing this sampler Example output:
233: * Tcp://Tcp.nowhere.com/pub/README.txt
234: *
235: * @return a formatted string label describing this sampler
236: */
237: public String getLabel() {
238: return ("tcp://" + this .getServer() + ":" + this .getPort());//$NON-NLS-1$
239: }
240:
241: private static String getClassname() {
242: String className = JMeterUtils.getPropDefault("tcp.handler",
243: "TCPClientImpl");
244: return className;
245: }
246:
247: private static final String protoPrefix = "org.apache.jmeter.protocol.tcp.sampler.";
248:
249: private Class getClass(String className) {
250: Class c = null;
251: try {
252: c = Class.forName(className, false, Thread.currentThread()
253: .getContextClassLoader());
254: } catch (ClassNotFoundException e) {
255: try {
256: c = Class.forName(protoPrefix + className, false,
257: Thread.currentThread().getContextClassLoader());
258: } catch (ClassNotFoundException e1) {
259: log.error("Could not find protocol class " + className);
260: }
261: }
262: return c;
263:
264: }
265:
266: private TCPClient getProtocol() {
267: TCPClient TCPClient = null;
268: Class javaClass = getClass(getClassname());
269: try {
270: TCPClient = (TCPClient) javaClass.newInstance();
271: if (log.isDebugEnabled()) {
272: log.debug(this + "Created: " + getClassname() + "@"
273: + Integer.toHexString(TCPClient.hashCode()));
274: }
275: } catch (Exception e) {
276: log.error(this + " Exception creating: " + getClassname(),
277: e);
278: }
279: return TCPClient;
280: }
281:
282: public SampleResult sample(Entry e)// Entry tends to be ignored ...
283: {
284: log.debug(getLabel() + " " + getFilename() + " "
285: + getUsername() + " " + getPassword());
286: SampleResult res = new SampleResult();
287: boolean isSuccessful = false;
288: res.setSampleLabel(getName());// Use the test element name for the
289: // label
290: res.setSamplerData("Host: " + getServer() + " Port: "
291: + getPort());
292: res.sampleStart();
293: try {
294: Socket sock = getSocket();
295: if (sock == null) {
296: res.setResponseCode("500");
297: res.setResponseMessage(getError());
298: } else {
299: InputStream is = sock.getInputStream();
300: OutputStream os = sock.getOutputStream();
301: String req = getRequestData();
302: // TODO handle filenames
303: res.setSamplerData(req);
304: protocolHandler.write(os, req);
305: String in = protocolHandler.read(is);
306: res.setResponseData(in.getBytes());
307: res.setDataType(SampleResult.TEXT);
308: res.setResponseCodeOK();
309: res.setResponseMessage("OK");
310: isSuccessful = true;
311: // Reset the status code if the message contains one
312: if (STATUS_PREFIX.length() > 0) {
313: int i = in.indexOf(STATUS_PREFIX);
314: int j = in.indexOf(STATUS_SUFFIX, i
315: + STATUS_PREFIX.length());
316: if (i != -1 && j > i) {
317: String rc = in.substring(i
318: + STATUS_PREFIX.length(), j);
319: res.setResponseCode(rc);
320: isSuccessful = checkResponseCode(rc);
321: if (haveStatusProps) {
322: res
323: .setResponseMessage(statusProps
324: .getProperty(rc,
325: "Status code not found in properties"));
326: } else {
327: res
328: .setResponseMessage("No status property file");
329: }
330: } else {
331: res.setResponseCode("999");
332: res
333: .setResponseMessage("Status value not found");
334: isSuccessful = false;
335: }
336: }
337: }
338: } catch (IOException ex) {
339: log.debug("", ex);
340: res.setResponseCode("500");
341: res.setResponseMessage(ex.toString());
342: closeSocket();
343: } finally {
344: // Calculate response time
345: res.sampleEnd();
346:
347: // Set if we were successful or not
348: res.setSuccessful(isSuccessful);
349:
350: if (!isReUseConnection()) {
351: closeSocket();
352: }
353: }
354:
355: return res;
356: }
357:
358: /**
359: * @param rc response code
360: * @return whether this represents success or not
361: */
362: private boolean checkResponseCode(String rc) {
363: if (rc.compareTo("400") >= 0 && rc.compareTo("499") <= 0) {
364: return false;
365: }
366: if (rc.compareTo("500") >= 0 && rc.compareTo("599") <= 0) {
367: return false;
368: }
369: return true;
370: }
371:
372: public void threadStarted() {
373: log.debug("Thread Started");
374: }
375:
376: private void closeSocket() {
377: Map cp = (Map) tp.get();
378: Socket con = (Socket) cp.remove(TCPKEY);
379: if (con != null) {
380: log.debug(this + " Closing connection " + con); //$NON-NLS-1$
381: try {
382: con.close();
383: } catch (IOException e) {
384: log.warn("Error closing socket " + e);
385: }
386: }
387: }
388:
389: public void threadFinished() {
390: log.debug("Thread Finished");
391: closeSocket();
392: }
393: }
|