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.java.sampler;
020:
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.Set;
024:
025: import org.apache.jmeter.config.Arguments;
026: import org.apache.jmeter.engine.event.LoopIterationEvent;
027: import org.apache.jmeter.samplers.AbstractSampler;
028: import org.apache.jmeter.samplers.Entry;
029: import org.apache.jmeter.samplers.SampleResult;
030: import org.apache.jmeter.testelement.TestElement;
031: import org.apache.jmeter.testelement.TestListener;
032: import org.apache.jmeter.testelement.property.TestElementProperty;
033: import org.apache.jorphan.logging.LoggingManager;
034: import org.apache.log.Logger;
035:
036: /**
037: * A sampler for executing custom Java code in each sample. See
038: * {@link JavaSamplerClient} and {@link AbstractJavaSamplerClient} for
039: * information on writing Java code to be executed by this sampler.
040: *
041: * @author <a href="mailto:jeremy_a@bigfoot.com">Jeremy Arnold</a>
042: */
043: public class JavaSampler extends AbstractSampler implements
044: TestListener {
045:
046: private static final long serialVersionUID = 221L; // Remember to change this when the class changes ...
047:
048: /**
049: * Property key representing the classname of the JavaSamplerClient to user.
050: */
051: public static final String CLASSNAME = "classname";
052:
053: /**
054: * Property key representing the arguments for the JavaSamplerClient.
055: */
056: public static final String ARGUMENTS = "arguments";
057:
058: /**
059: * The JavaSamplerClient instance used by this sampler to actually perform
060: * the sample.
061: */
062: private transient JavaSamplerClient javaClient = null;
063:
064: /**
065: * The JavaSamplerContext instance used by this sampler to hold information
066: * related to the test run, such as the parameters specified for the sampler
067: * client.
068: */
069: private transient JavaSamplerContext context = null;
070:
071: /**
072: * Logging
073: */
074: private static transient Logger log = LoggingManager
075: .getLoggerForClass();
076:
077: /**
078: * Set used to register all active JavaSamplers. This is used so that the
079: * samplers can be notified when the test ends.
080: */
081: private static Set allSamplers = new HashSet();
082:
083: /**
084: * Create a JavaSampler.
085: */
086: public JavaSampler() {
087: setArguments(new Arguments());
088: synchronized (allSamplers) {
089: allSamplers.add(this );
090: }
091: }
092:
093: /**
094: * Set the arguments (parameters) for the JavaSamplerClient to be executed
095: * with.
096: *
097: * @param args
098: * the new arguments. These replace any existing arguments.
099: */
100: public void setArguments(Arguments args) {
101: setProperty(new TestElementProperty(ARGUMENTS, args));
102: }
103:
104: /**
105: * Get the arguments (parameters) for the JavaSamplerClient to be executed
106: * with.
107: *
108: * @return the arguments
109: */
110: public Arguments getArguments() {
111: return (Arguments) getProperty(ARGUMENTS).getObjectValue();
112: }
113:
114: /**
115: * Releases Java Client.
116: */
117: private void releaseJavaClient() {
118: if (javaClient != null) {
119: javaClient.teardownTest(context);
120: }
121: javaClient = null;
122: context = null;
123: }
124:
125: /**
126: * Sets the Classname attribute of the JavaConfig object
127: *
128: * @param classname
129: * the new Classname value
130: */
131: public void setClassname(String classname) {
132: setProperty(CLASSNAME, classname);
133: }
134:
135: /**
136: * Gets the Classname attribute of the JavaConfig object
137: *
138: * @return the Classname value
139: */
140: public String getClassname() {
141: return getPropertyAsString(CLASSNAME);
142: }
143:
144: /**
145: * Performs a test sample.
146: *
147: * The <code>sample()</code> method retrieves the reference to the Java
148: * client and calls its <code>runTest()</code> method.
149: *
150: * @see JavaSamplerClient#runTest(JavaSamplerContext)
151: *
152: * @param entry
153: * the Entry for this sample
154: * @return test SampleResult
155: */
156: public SampleResult sample(Entry entry) {
157: Arguments args = getArguments();
158: args.addArgument(TestElement.NAME, getName()); // Allow Sampler access
159: // to test element name
160: context = new JavaSamplerContext(args);
161: if (javaClient == null) {
162: log.debug(whoAmI() + "Creating Java Client");
163: createJavaClient();
164: javaClient.setupTest(context);
165: }
166:
167: SampleResult result = createJavaClient().runTest(context);
168:
169: // Only set the default label if it has not been set
170: if (result != null && result.getSampleLabel().length() == 0) {
171: result.setSampleLabel(getName());
172: }
173:
174: return result;
175: }
176:
177: /**
178: * Returns reference to <code>JavaSamplerClient</code>.
179: *
180: * The <code>createJavaClient()</code> method uses reflection to create an
181: * instance of the specified Java protocol client. If the class can not be
182: * found, the method returns a reference to <code>this</code> object.
183: *
184: * @return JavaSamplerClient reference.
185: */
186: private JavaSamplerClient createJavaClient() {
187: if (javaClient == null) {
188: try {
189: Class javaClass = Class.forName(getClassname().trim(),
190: false, Thread.currentThread()
191: .getContextClassLoader());
192: javaClient = (JavaSamplerClient) javaClass
193: .newInstance();
194: context = new JavaSamplerContext(getArguments());
195:
196: if (log.isDebugEnabled()) {
197: log
198: .debug(whoAmI()
199: + "\tCreated:\t"
200: + getClassname()
201: + "@"
202: + Integer.toHexString(javaClient
203: .hashCode()));
204: }
205: } catch (Exception e) {
206: log.error(whoAmI() + "\tException creating: "
207: + getClassname(), e);
208: javaClient = new ErrorSamplerClient();
209: }
210: }
211: return javaClient;
212: }
213:
214: /**
215: * Retrieves reference to JavaSamplerClient.
216: *
217: * Convience method used to check for null reference without actually
218: * creating a JavaSamplerClient
219: *
220: * @return reference to JavaSamplerClient NOTUSED private JavaSamplerClient
221: * retrieveJavaClient() { return javaClient; }
222: */
223:
224: /**
225: * Generate a String identifier of this instance for debugging purposes.
226: *
227: * @return a String identifier for this sampler instance
228: */
229: private String whoAmI() {
230: StringBuffer sb = new StringBuffer();
231: sb.append(Thread.currentThread().getName());
232: sb.append("@");
233: sb.append(Integer.toHexString(hashCode()));
234: sb.append("-");
235: sb.append(getName());
236: return sb.toString();
237: }
238:
239: // TestListener implementation
240: /* Implements TestListener.testStarted() */
241: public void testStarted() {
242: log.debug(whoAmI() + "\ttestStarted");
243: }
244:
245: /* Implements TestListener.testStarted(String) */
246: public void testStarted(String host) {
247: log.debug(whoAmI() + "\ttestStarted(" + host + ")");
248: }
249:
250: /**
251: * Method called at the end of the test. This is called only on one instance
252: * of JavaSampler. This method will loop through all of the other
253: * JavaSamplers which have been registered (automatically in the
254: * constructor) and notify them that the test has ended, allowing the
255: * JavaSamplerClients to cleanup.
256: */
257: public void testEnded() {
258: log.debug(whoAmI() + "\ttestEnded");
259: synchronized (allSamplers) {
260: Iterator i = allSamplers.iterator();
261: while (i.hasNext()) {
262: JavaSampler sampler = (JavaSampler) i.next();
263: sampler.releaseJavaClient();
264: i.remove();
265: }
266: }
267: }
268:
269: /* Implements TestListener.testEnded(String) */
270: public void testEnded(String host) {
271: testEnded();
272: }
273:
274: /* Implements TestListener.testIterationStart(LoopIterationEvent) */
275: public void testIterationStart(LoopIterationEvent event) {
276: }
277:
278: /**
279: * A {@link JavaSamplerClient} implementation used for error handling. If an
280: * error occurs while creating the real JavaSamplerClient object, it is
281: * replaced with an instance of this class. Each time a sample occurs with
282: * this class, the result is marked as a failure so the user can see that
283: * the test failed.
284: */
285: class ErrorSamplerClient extends AbstractJavaSamplerClient {
286: /**
287: * Return SampleResult with data on error.
288: *
289: * @see JavaSamplerClient#runTest(JavaSamplerContext)
290: */
291: public SampleResult runTest(JavaSamplerContext p_context) {
292: log.debug(whoAmI() + "\trunTest");
293: Thread.yield();
294: SampleResult results = new SampleResult();
295: results.setSuccessful(false);
296: results
297: .setResponseData(("Class not found: " + getClassname())
298: .getBytes());
299: results.setSampleLabel("ERROR: " + getClassname());
300: return results;
301: }
302: }
303: }
|