001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.apache.jmeter.module.integration;
043:
044: import java.awt.Component;
045: import java.awt.Image;
046: import java.io.BufferedInputStream;
047: import java.io.BufferedWriter;
048: import java.io.File;
049: import java.io.FileInputStream;
050: import java.io.FileWriter;
051: import java.io.FilenameFilter;
052: import java.io.IOException;
053: import java.io.InputStream;
054: import java.net.URL;
055: import java.util.ArrayList;
056: import java.util.Collection;
057: import java.util.Collections;
058: import java.util.Iterator;
059: import java.util.LinkedList;
060: import java.util.List;
061: import java.util.Locale;
062: import java.util.Map;
063: import java.util.WeakHashMap;
064: import java.util.concurrent.Semaphore;
065: import javax.swing.JPopupMenu;
066:
067: import org.apache.jmeter.engine.JMeterEngine;
068: import org.apache.jmeter.engine.StandardJMeterEngine;
069: import org.apache.jmeter.engine.TreeCloner;
070: import org.apache.jmeter.engine.event.LoopIterationEvent;
071: import org.apache.jmeter.module.JMXTypeDataNode;
072: import org.apache.jmeter.module.exceptions.InitializationException;
073: import org.apache.jmeter.module.loadgenerator.spi.impl.ProcessDescriptor;
074: import org.apache.jmeter.reporters.ResultCollector;
075: import org.apache.jmeter.save.SaveService;
076: import org.apache.jmeter.testelement.TestElement;
077: import org.apache.jmeter.testelement.TestListener;
078: import org.apache.jmeter.testelement.TestPlan;
079: import org.apache.jmeter.util.JMeterUtils;
080: import org.apache.jmeter.visualizers.Visualizer;
081: import org.apache.jorphan.collections.HashTree;
082: import org.apache.jorphan.collections.HashTreeTraverser;
083: import org.openide.filesystems.FileAttributeEvent;
084: import org.openide.filesystems.FileChangeListener;
085: import org.openide.filesystems.FileEvent;
086: import org.openide.filesystems.FileObject;
087: import org.openide.filesystems.FileRenameEvent;
088: import org.openide.filesystems.FileUtil;
089: import org.openide.modules.InstalledFileLocator;
090: import org.openide.util.Utilities;
091:
092: /**
093: *
094: * @author Jaroslav Bachorik
095: */
096: public class JMeterIntegrationEngine {
097: private static JMeterIntegrationEngine instance;
098:
099: private static Semaphore instanceLock = new Semaphore(1);
100: private Semaphore parserLock = new Semaphore(1);
101:
102: private Map<String, HashTree> testPlans;
103:
104: private Collection<ProcessDescriptor> processes;
105:
106: private static String jmeterPath;
107:
108: private class TestProcessListener implements TestListener {
109: private ProcessDescriptor descriptor = null;
110:
111: public TestProcessListener(final ProcessDescriptor process) {
112: descriptor = process;
113: }
114:
115: public void testStarted(String string) {
116: descriptor.setRunning(true);
117: }
118:
119: public void testEnded(String string) {
120: descriptor.setRunning(false);
121: }
122:
123: public void testIterationStart(
124: LoopIterationEvent loopIterationEvent) {
125: }
126:
127: public void testStarted() {
128: descriptor.setRunning(true);
129: }
130:
131: public void testEnded() {
132: descriptor.setRunning(false);
133: }
134:
135: }
136:
137: private static String jmeterCP = System
138: .getProperty("java.class.path");
139:
140: /** Creates a new instance of JMeterIntegrationEngine */
141: private JMeterIntegrationEngine() {
142: testPlans = new WeakHashMap<String, HashTree>();
143: processes = Collections
144: .synchronizedCollection(new ArrayList<ProcessDescriptor>());
145: }
146:
147: /**
148: * Singleton getter
149: */
150: public static JMeterIntegrationEngine getDefault()
151: throws InitializationException {
152: if (instance == null) {
153: try {
154: instanceLock.acquire();
155: if (instance == null) {
156: instance = new JMeterIntegrationEngine();
157: instance.initJMeter();
158: }
159: } catch (InterruptedException ex) {
160: ex.printStackTrace();
161: throw new InitializationException();
162: } catch (Exception e) {
163: e.printStackTrace();
164: } finally {
165: instanceLock.release();
166: }
167: }
168:
169: String classpath = System.getProperty("java.class.path");
170: if (classpath.indexOf("ApacheJMeter_core") == -1) {
171: System.setProperty("java.class.path", classpath
172: + File.pathSeparator + jmeterCP);
173: }
174: return instance;
175: }
176:
177: public List<TestElement> getChildren(final TestElement parent,
178: final String testPlan) {
179: List<TestElement> children = new ArrayList<TestElement>();
180:
181: for (Object elementObj : getPlanTree(testPlan, false).list(
182: parent)) {
183: children.add((TestElement) elementObj);
184: }
185:
186: return children;
187: }
188:
189: public List<TestElement> getChildren(
190: final List<TestElement> elementPath, final String testPlan) {
191: List<TestElement> children = new ArrayList<TestElement>();
192:
193: for (Object elementObj : getPlanTree(testPlan, false).list(
194: elementPath)) {
195: children.add((TestElement) elementObj);
196: }
197:
198: return children;
199: }
200:
201: public JMeterPlan getPlan(final String testPlan) {
202: HashTree planTree = getPlanTree(testPlan, false);
203:
204: return new JMeterPlan(testPlan, planTree, (TestPlan) planTree
205: .getArray()[0]);
206: }
207:
208: public ProcessDescriptor runTestPlan(final String testPlan) {
209: HashTree planTree = getPlanTree(testPlan, true);
210: TestElement root = (TestElement) planTree.getArray()[0];
211:
212: JMeterEngine engine = new StandardJMeterEngine();
213:
214: planTree.traverse(new HashTreeTraverser() {
215: public void addNode(Object object, HashTree hashTree) {
216: if (object instanceof TestElement) {
217: if (object instanceof ResultCollector) {
218: ResultCollector collector = (ResultCollector) object;
219: collector
220: .setListener((Visualizer) getElementCustomizer(collector));
221: }
222: }
223: }
224:
225: public void processPath() {
226: }
227:
228: public void subtractNode() {
229: }
230: });
231:
232: TreeCloner cloner = new TreeCloner(false);
233: planTree.traverse(cloner);
234:
235: ProcessDescriptor process = new ProcessDescriptor(engine,
236: testPlan, root.getPropertyAsString(TestElement.NAME),
237: true);
238: HashTree runtimeTree = cloner.getClonedTree();
239:
240: runtimeTree.add(new TestProcessListener(process));
241:
242: engine.configure(runtimeTree);
243: try {
244: engine.runTest();
245: processes.add(process);
246: } catch (Exception e) {
247: e.printStackTrace();
248: process = null;
249: }
250:
251: return process;
252: }
253:
254: public ProcessDescriptor prepareTest(final String testPlan) {
255: HashTree planTree = getPlanTree(testPlan, true);
256: TestElement root = (TestElement) planTree.getArray()[0];
257:
258: JMeterEngine engine = new StandardJMeterEngine();
259:
260: planTree.traverse(new HashTreeTraverser() {
261: public void addNode(Object object, HashTree hashTree) {
262: if (object instanceof TestElement) {
263: if (object instanceof ResultCollector) {
264: ResultCollector collector = (ResultCollector) object;
265: collector
266: .setListener((Visualizer) getElementCustomizer(collector));
267: }
268: }
269: }
270:
271: public void processPath() {
272: }
273:
274: public void subtractNode() {
275: }
276: });
277:
278: TreeCloner cloner = new TreeCloner(false);
279: planTree.traverse(cloner);
280:
281: ProcessDescriptor process = new ProcessDescriptor(engine,
282: testPlan, root.getPropertyAsString(TestElement.NAME),
283: true);
284: HashTree runtimeTree = cloner.getClonedTree();
285:
286: TestPlan plan = (TestPlan) runtimeTree.getArray()[0];
287: Map userVariables = plan.getUserDefinedVariables();
288:
289: if (userVariables.containsKey("nb.enabled")) {
290: process.setNbReady(true);
291: process.setThreadsCount(Integer
292: .parseInt((String) userVariables.get("nb.users")));
293: process.setRampup(Integer.parseInt((String) userVariables
294: .get("nb.rampup")));
295: process.setInterleave(Integer
296: .parseInt((String) userVariables
297: .get("nb.interleave")));
298: }
299:
300: runtimeTree.add(runtimeTree.getArray()[0],
301: new TestProcessListener(process));
302: engine.configure(runtimeTree);
303:
304: return process;
305: }
306:
307: public void runTestPlan(final ProcessDescriptor descriptor) {
308: try {
309: descriptor.getEngine().runTest();
310: } catch (Exception e) {
311: e.printStackTrace();
312: }
313: }
314:
315: public void stopTestPlan(final ProcessDescriptor descriptor) {
316: descriptor.getEngine().stopTest();
317: }
318:
319: public Collection<ProcessDescriptor> getProcesses() {
320: Collection<ProcessDescriptor> result = new ArrayList<ProcessDescriptor>();
321: result.addAll(processes);
322:
323: return result;
324: }
325:
326: public Component getElementCustomizer(final TestElement element) {
327: return JMeterGUISupport.getDefault().getGui(element);
328: }
329:
330: public Image getElementIcon(final TestElement element) {
331: return JMeterGUISupport.getDefault().getIcon(element);
332: }
333:
334: public JPopupMenu getElementMenu(final TestElement element) {
335: return JMeterGUISupport.getDefault().getPopup(element);
336: }
337:
338: public void add(final List<TestElement> parentPath,
339: final TestElement child, final String testPlan) {
340: HashTree planTree = getPlanTree(testPlan, false);
341: HashTree newTree = planTree.getTree(parentPath);
342:
343: if (planTree != null) {
344: planTree.getTree(parentPath).add(child);
345: }
346: }
347:
348: public HashTree getPlanTree(final String testPlan,
349: final boolean forceReload) {
350: HashTree planTree = forceReload ? null : testPlans
351: .get(testPlan);
352:
353: if (planTree == null) {
354: try {
355: parserLock.acquire();
356: if (planTree == null) {
357: planTree = parseJMeterTree(testPlan);
358: testPlans.put(testPlan, planTree);
359: }
360: } catch (InterruptedException e) {
361: e.printStackTrace();
362: } finally {
363: parserLock.release();
364: }
365: }
366:
367: return planTree;
368: }
369:
370: public boolean savePlan(final JMeterPlan plan) {
371: BufferedWriter writer = null;
372: try {
373: writer = new BufferedWriter(new FileWriter(plan.getPath()));
374: SaveService.saveTree(plan.getTree(), writer);
375: } catch (Exception e) {
376: e.printStackTrace();
377: return false;
378: } finally {
379: if (writer != null) {
380: try {
381: writer.close();
382: } catch (IOException e) {
383: }
384: }
385: }
386: return true;
387: }
388:
389: public Process externalEdit(final String scriptPath)
390: throws IOException {
391: // final String cmdProcessor = (Utilities.isWindows() ? "call" : "sh");
392: final String jmeterRoot = jmeterPath + File.separator + "bin";
393: final String jmeterExecutable = jmeterRoot + File.separator
394: + "jmeter" + (Utilities.isWindows() ? ".bat" : "");
395:
396: String[] params = null;
397: // System.out.println("Userdir = " + jmeterRoot);
398: // System.out.println("Scirptpath = " + decoratePath(scriptPath));
399:
400: if (Utilities.isWindows()) {
401: params = new String[] { jmeterExecutable, "-t",
402: decoratePath(scriptPath) };
403: } else {
404: params = new String[] { "sh", jmeterExecutable, "-t",
405: decoratePath(scriptPath) };
406: }
407: return Runtime.getRuntime().exec(params, null,
408: new File(jmeterRoot));
409: }
410:
411: public void cleanup() {
412: Iterator<ProcessDescriptor> iter = processes.iterator();
413: while (iter.hasNext()) {
414: ProcessDescriptor desc = iter.next();
415: if (!desc.isRunning()) {
416: iter.remove();
417: }
418: }
419: }
420:
421: public static String getLogPath() {
422: return JMeterUtils.getJMeterHome() + File.separator
423: + "summariser.log"; //NOI18N
424: }
425:
426: public static void clearLog() {
427: // try {
428: // File file = new File(getLogPath());
429: // if (file.exists()) {
430: // file.delete();
431: // }
432: // if (file.createNewFile()) {
433: // LoggingManager.setTarget(new FileWriter(file));
434: // }
435: // } catch (IOException e) {
436: // // IGNORE
437: // }
438: }
439:
440: /**
441: * Initializes JMeter subsystem - sets all properties required by JMeter and calls static methods to initialize JMeter internals
442: */
443: private static void initJMeter() throws InitializationException {
444: // try to locate the module jar
445: URL rsrc = JMXTypeDataNode.class
446: .getResource("/org/apache/jmeter/JMeter.class"); // NOI18N
447:
448: File userDir = InstalledFileLocator.getDefault().locate(
449: "modules/jmeter/bin/jmeter.properties",
450: "org.apache.jmeter.module", false); // NOI18N
451: try {
452: jmeterPath = userDir.getCanonicalPath();
453: // System.out.println("Calculated JMeter path = " + jmeterPath);
454: jmeterPath = jmeterPath.substring(0, jmeterPath
455: .lastIndexOf("modules" + File.separator + "jmeter")
456: + ("modules" + File.separator + "jmeter").length()); // NOI18N
457: // System.out.println("Modified JMeter path = " + jmeterPath);
458: // change user.dir property - it's required by JMeter
459: // System.setProperty("user.dir", jmeterPath + File.separator + "bin"); // NOI18N
460: // set JMeter home
461: JMeterUtils.setJMeterHome(jmeterPath);
462: // add plugins to classpath
463: final String extPath = jmeterPath + File.separator + "lib"
464: + File.separator + "ext";
465: File dir = new File(extPath);
466: String[] jars = dir.list(new FilenameFilter() {
467: public boolean accept(File f, String name) {
468: if (name.endsWith(".jar")) {
469: return true;
470: }
471: return false;
472: }
473: });
474: String classPath = System.getProperty("java.class.path");
475: String separator = System.getProperty("path.separator");
476:
477: StringBuffer newClassPath = new StringBuffer();
478: for (String jar : jars) {
479: newClassPath.append(separator).append(extPath).append(
480: File.separator).append(jar);
481: }
482: jmeterCP = newClassPath.toString();
483:
484: // call getProperties - this call also initializes the properties (pretty nasty hack)
485: JMeterUtils.getProperties(userDir.getCanonicalPath());
486: //
487: // // initialize the rest of JMeter - JMeter initializes some static fields within the constructor -> it's hell
488: JMeterUtils.setLocale(Locale.getDefault());
489: // LoggingManager.setTarget(new FileWriter(getLogPath()));
490: } catch (IOException e) {
491: throw new InitializationException(e);
492: }
493: }
494:
495: private HashTree parseJMeterTree(final String planPath) {
496: FileObject planFile = FileUtil.toFileObject(new File(planPath));
497: return parseJMeterTree(planFile);
498: }
499:
500: private HashTree parseJMeterTree(final FileObject file) {
501: InputStream planInputStream = null;
502: HashTree rootTree = null;
503: try {
504: planInputStream = new BufferedInputStream(
505: new FileInputStream(FileUtil.toFile(file)));
506: rootTree = SaveService.loadTree(planInputStream);
507:
508: // Remove the disabled items
509: convertSubTree(rootTree);
510:
511: } catch (Exception e) {
512: e.printStackTrace();
513: }
514:
515: return rootTree;
516: }
517:
518: // no clue about the purpose; JMeter requires it
519: private void convertSubTree(HashTree tree) {
520: Iterator iter = new LinkedList(tree.list()).iterator();
521: while (iter.hasNext()) {
522: TestElement item = (TestElement) iter.next();
523: if (!item.isEnabled() || item instanceof ResultCollector) {
524: tree.remove(item);
525: } else {
526: if (item instanceof TestPlan) {
527: TestPlan tp = (TestPlan) item;
528: tp.setFunctionalMode(tp.isFunctionalMode());
529: tp.setSerialized(tp.isSerialized());
530: }
531: convertSubTree(tree.getTree(item));
532: }
533: }
534: }
535:
536: private String decoratePath(final String path) {
537: if (Utilities.isWindows() && path.indexOf(' ') > -1) {
538: return "\"" + path + "\"";
539: }
540: return path;
541: }
542: }
|