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, WITHOUT
013: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
014: * License for the specific language governing permissions and limitations
015: * under the License.
016: *
017: */
018:
019: package org.apache.jmeter.protocol.http.sampler;
020:
021: import org.apache.jmeter.protocol.http.control.CookieManager;
022: import org.apache.jmeter.protocol.http.util.accesslog.Filter;
023: import org.apache.jmeter.protocol.http.util.accesslog.LogParser;
024: import org.apache.jmeter.samplers.Entry;
025: import org.apache.jmeter.samplers.SampleResult;
026: import org.apache.jmeter.testbeans.TestBean;
027: import org.apache.jmeter.testelement.TestCloneable;
028: import org.apache.jmeter.testelement.ThreadListener;
029: import org.apache.jmeter.threads.JMeterContextService;
030: import org.apache.jorphan.logging.LoggingManager;
031: import org.apache.jorphan.util.JMeterException;
032: import org.apache.log.Logger;
033:
034: /**
035: * Description: <br>
036: * <br>
037: * AccessLogSampler is responsible for a couple of things:
038: * <p>
039: * <ul>
040: * <li>creating instances of Generator
041: * <li>creating instances of Parser
042: * <li>triggering popup windows
043: * <li>calling Generator.generateRequest()
044: * <li>checking to make sure the classes are valid
045: * <li>making sure a class can be instantiated
046: * </ul>
047: * The intent of this sampler is it uses the generator and parser to create a
048: * HTTPSampler when it is needed. It does not contain logic about how to parse
049: * the logs. It also doesn't care how Generator is implemented, as long as it
050: * implements the interface. This means a person could simply implement a dummy
051: * parser to generate random parameters and the generator consumes the results.
052: * This wasn't the original intent of the sampler. I originaly wanted to write
053: * this sampler, so that I can take production logs to simulate production
054: * traffic in a test environment. Doing so is desirable to study odd or unusual
055: * behavior. It's also good to compare a new system against an existing system
056: * to get near apples- to-apples comparison. I've been asked if benchmarks are
057: * really fair comparisons just about every single time, so this helps me
058: * accomplish that task.
059: * <p>
060: * Some bugs only appear under production traffic, so it is useful to generate
061: * traffic using production logs. This way, JMeter can record when problems
062: * occur and provide a way to match the server logs.
063: * <p>
064: * Created on: Jun 26, 2003
065: *
066: * @author Peter Lin
067: */
068: public class AccessLogSampler extends HTTPSampler implements TestBean,
069: ThreadListener {
070: private static Logger log = LoggingManager.getLoggerForClass();
071:
072: private static final long serialVersionUID = 221L; // Remember to change this when the class changes ...
073:
074: public static final String DEFAULT_CLASS = "org.apache.jmeter.protocol.http.util.accesslog.TCLogParser"; // $NON-NLS-1$
075:
076: /** private members used by class * */
077: transient private LogParser PARSER = null;
078:
079: // NOTUSED private Class PARSERCLASS = null;
080: private String logFile, parserClassName, filterClassName;
081:
082: transient private Filter filter;
083:
084: private int count = 0;
085:
086: private boolean started = false;
087:
088: /**
089: * Set the path where XML messages are stored for random selection.
090: */
091: public void setLogFile(String path) {
092: logFile = path;
093: }
094:
095: /**
096: * Get the path where XML messages are stored. this is the directory where
097: * JMeter will randomly select a file.
098: */
099: public String getLogFile() {
100: return logFile;
101: }
102:
103: /**
104: * it's kinda obvious, but we state it anyways. Set the xml file with a
105: * string path.
106: *
107: * @param classname -
108: * parser class name
109: */
110: public void setParserClassName(String classname) {
111: parserClassName = classname;
112: }
113:
114: /**
115: * Get the file location of the xml file.
116: *
117: * @return String file path.
118: */
119: public String getParserClassName() {
120: return parserClassName;
121: }
122:
123: /**
124: * sample gets a new HTTPSampler from the generator and calls it's sample()
125: * method.
126: */
127: public SampleResult sampleWithParser() {
128: initFilter();
129: instantiateParser();
130: SampleResult res = null;
131: try {
132:
133: if (PARSER == null)
134: throw new JMeterException("No Parser available");
135: /*
136: * samp.setDomain(this.getDomain()); samp.setPort(this.getPort());
137: */
138: // we call parse with 1 to get only one.
139: // this also means if we change the implementation
140: // to use 2, it would use every other entry and
141: // so on. Not that it is really useful, but a
142: // person could use it that way if they have a
143: // huge gigabyte log file and they only want to
144: // use a quarter of the entries.
145: int this Count = PARSER.parseAndConfigure(1, this );
146: if (this Count < 0) // Was there an error?
147: {
148: return errorResult(new Error(
149: "Problem parsing the log file"),
150: new HTTPSampleResult());
151: }
152: if (this Count == 0) {
153: if (count == 0 || filter == null) {
154: JMeterContextService.getContext().getThread()
155: .stop();
156: }
157: if (filter != null)
158: filter.reset();
159: CookieManager cm = getCookieManager();
160: if (cm != null)
161: cm.clear();
162: count = 0;
163: return errorResult(new Error("No entries found"),
164: new HTTPSampleResult());
165: }
166: count = this Count;
167: res = sample();
168: res.setSampleLabel(toString());
169: } catch (Exception e) {
170: log.warn("Sampling failure", e);
171: return errorResult(e, new HTTPSampleResult());
172: }
173: return res;
174: }
175:
176: /**
177: * sample(Entry e) simply calls sample().
178: *
179: * @param e -
180: * ignored
181: * @return the new sample
182: */
183: public SampleResult sample(Entry e) {
184: return sampleWithParser();
185: }
186:
187: /**
188: * Method will instantiate the log parser based on the class in the text
189: * field. This was done to make it easier for people to plugin their own log
190: * parser and use different log parser.
191: */
192: public void instantiateParser() {
193: if (PARSER == null) {
194: try {
195: if (this .getParserClassName() != null
196: && this .getParserClassName().length() > 0) {
197: if (this .getLogFile() != null
198: && this .getLogFile().length() > 0) {
199: PARSER = (LogParser) Class.forName(
200: getParserClassName()).newInstance();
201: PARSER.setSourceFile(this .getLogFile());
202: PARSER.setFilter(filter);
203: } else {
204: log.error("No log file specified");
205: }
206: }
207: } catch (InstantiationException e) {
208: log.error("", e);
209: } catch (IllegalAccessException e) {
210: log.error("", e);
211: } catch (ClassNotFoundException e) {
212: log.error("", e);
213: }
214: }
215: }
216:
217: /**
218: * @return Returns the filterClassName.
219: */
220: public String getFilterClassName() {
221: return filterClassName;
222: }
223:
224: /**
225: * @param filterClassName
226: * The filterClassName to set.
227: */
228: public void setFilterClassName(String filterClassName) {
229: this .filterClassName = filterClassName;
230: }
231:
232: /**
233: * @return Returns the domain.
234: */
235: public String getDomain() {
236: return super .getDomain();
237: }
238:
239: /**
240: * @param domain
241: * The domain to set.
242: */
243: public void setDomain(String domain) {
244: super .setDomain(domain);
245: }
246:
247: /**
248: * @return Returns the imageParsing.
249: */
250: public boolean isImageParsing() {
251: return super .isImageParser();
252: }
253:
254: /**
255: * @param imageParsing
256: * The imageParsing to set.
257: */
258: public void setImageParsing(boolean imageParsing) {
259: super .setImageParser(imageParsing);
260: }
261:
262: /**
263: * @return Returns the port.
264: */
265: public String getPortString() {
266: return super .getPropertyAsString(HTTPSamplerBase.PORT);
267: }
268:
269: /**
270: * @param port
271: * The port to set.
272: */
273: public void setPortString(String port) {
274: super .setProperty(HTTPSamplerBase.PORT, port);
275: }
276:
277: /**
278: *
279: */
280: public AccessLogSampler() {
281: super ();
282: }
283:
284: protected void initFilter() {
285: if (filter == null && filterClassName != null
286: && filterClassName.length() > 0) {
287: try {
288: filter = (Filter) Class.forName(filterClassName)
289: .newInstance();
290: } catch (Exception e) {
291: log.warn("Couldn't instantiate filter '"
292: + filterClassName + "'", e);
293: }
294: }
295: }
296:
297: /*
298: * (non-Javadoc)
299: *
300: * @see java.lang.Object#clone()
301: */
302: public Object clone() {
303: AccessLogSampler s = (AccessLogSampler) super .clone();
304: if (started) {
305: if (filterClassName != null && filterClassName.length() > 0) {
306:
307: try {
308: if (TestCloneable.class.isAssignableFrom(Class
309: .forName(filterClassName))) {
310: initFilter();
311: s.filter = (Filter) ((TestCloneable) filter)
312: .clone();
313: }
314: if (TestCloneable.class.isAssignableFrom(Class
315: .forName(parserClassName))) {
316: instantiateParser();
317: s.PARSER = (LogParser) ((TestCloneable) PARSER)
318: .clone();
319: if (filter != null) {
320: s.PARSER.setFilter(s.filter);
321: }
322: }
323: } catch (Exception e) {
324: log.warn("Could not clone cloneable filter", e);
325: }
326: }
327: }
328: return s;
329: }
330:
331: /*
332: * (non-Javadoc)
333: *
334: * @see org.apache.jmeter.testelement.TestListener#testEnded()
335: */
336: public void testEnded() {
337: if (PARSER != null) {
338: PARSER.close();
339: }
340: filter = null;
341: started = false;
342: super .testEnded();
343: }
344:
345: /*
346: * (non-Javadoc)
347: *
348: * @see org.apache.jmeter.testelement.TestListener#testStarted()
349: */
350: public void testStarted() {
351: started = true;
352: super .testStarted();
353: }
354:
355: /* (non-Javadoc)
356: * @see org.apache.jmeter.testelement.AbstractTestElement#threadFinished()
357: */
358: public void threadFinished() {
359: if (PARSER instanceof ThreadListener)
360: ((ThreadListener) PARSER).threadFinished();
361: if (filter instanceof ThreadListener)
362: ((ThreadListener) filter).threadFinished();
363: }
364: }
|