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.Container;
046: import java.awt.Image;
047: import java.awt.event.ActionEvent;
048: import java.awt.event.ActionListener;
049: import java.beans.PropertyChangeListener;
050: import java.beans.PropertyChangeSupport;
051: import java.io.OutputStreamWriter;
052: import java.util.Enumeration;
053: import java.util.Iterator;
054: import java.util.LinkedList;
055: import java.util.Properties;
056: import java.util.concurrent.Semaphore;
057:
058: import javax.swing.ImageIcon;
059: import javax.swing.JPopupMenu;
060: import javax.swing.JTextPane;
061: import javax.swing.SwingUtilities;
062: import javax.swing.event.TreeModelEvent;
063: import javax.swing.event.TreeModelListener;
064: import javax.swing.event.TreeSelectionEvent;
065: import javax.swing.tree.TreePath;
066:
067: import org.apache.jmeter.config.gui.AbstractConfigGui;
068: import org.apache.jmeter.control.gui.AbstractControllerGui;
069: import org.apache.jmeter.control.gui.TestPlanGui;
070: import org.apache.jmeter.control.gui.WorkBenchGui;
071: import org.apache.jmeter.gui.GUIFactory;
072: import org.apache.jmeter.gui.GuiPackage;
073: import org.apache.jmeter.gui.JMeterGUIComponent;
074: import org.apache.jmeter.gui.MainFrame;
075: import org.apache.jmeter.gui.action.ActionNames;
076: import org.apache.jmeter.gui.action.ActionRouter;
077: import org.apache.jmeter.gui.action.Load;
078: import org.apache.jmeter.gui.tree.JMeterTreeListener;
079: import org.apache.jmeter.gui.tree.JMeterTreeModel;
080: import org.apache.jmeter.gui.tree.JMeterTreeNode;
081: import org.apache.jmeter.plugin.JMeterPlugin;
082: import org.apache.jmeter.plugin.PluginManager;
083: import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
084: import org.apache.jmeter.save.SaveService;
085: import org.apache.jmeter.testelement.TestElement;
086: import org.apache.jmeter.threads.gui.ThreadGroupGui;
087: import org.apache.jmeter.timers.gui.AbstractTimerGui;
088: import org.apache.jmeter.util.JMeterUtils;
089: import org.apache.jmeter.visualizers.gui.AbstractVisualizer;
090: import org.apache.jorphan.collections.HashTree;
091: import org.apache.jorphan.util.JOrphanUtils;
092: import org.openide.filesystems.FileLock;
093: import org.openide.filesystems.FileObject;
094: import org.openide.filesystems.FileUtil;
095:
096: /**
097: *
098: * @author Jaroslav Bachorik
099: */
100: public class JMeterGUISupport implements JMeterPlugin {
101: public static final String DIRTY = "dirty";
102:
103: private static Semaphore initLock = new Semaphore(1);
104: private static JMeterGUISupport instance = null;
105: private static ThreadLocal currentNodeHolder = new ThreadLocal();
106:
107: private JMeterTreeModel treeModel = new JMeterTreeModel();
108:
109: private static TestElement lastSelectedElementCopy = null,
110: lastSelectedElement = null;
111:
112: private static boolean modified = false;
113: private static boolean editing = false;
114: private static boolean loading = false;
115:
116: private PropertyChangeSupport pcs = new PropertyChangeSupport(this );
117:
118: private final JMeterTreeListener modifiedListener = new JMeterTreeListener() {
119: public void valueChanged(final TreeSelectionEvent e) {
120: super .valueChanged(e);
121: SwingUtilities.invokeLater(new Runnable() {
122: public void run() {
123: TreePath path = e.getPath();
124: JMeterTreeNode node = (JMeterTreeNode) path
125: .getPathComponent(path.getPathCount() - 1);
126: TestElement element = (TestElement) node
127: .getUserObject();
128:
129: if (lastSelectedElement != null) {
130: if (lastSelectedElementCopy != null) {
131: if (!lastSelectedElement
132: .equals(lastSelectedElementCopy)) {
133: setDirty(true);
134: }
135: } else {
136: setDirty(true);
137: }
138: }
139:
140: lastSelectedElement = element;
141: lastSelectedElementCopy = (TestElement) element
142: .clone();
143: }
144: });
145: }
146:
147: };
148: private final JMeterTreeListener normalListener = new JMeterTreeListener();
149:
150: private GuiPackage jmeterGui = null;
151: private MainFrame frame = null;
152:
153: /** Creates a new instance of JMeterGUISupport */
154: private JMeterGUISupport() {
155: // NB proprietary EditorKit hack
156: final String editorClassBackup = JTextPane
157: .getEditorKitClassNameForContentType("text/html");
158: JTextPane.registerEditorKitForContentType("text/html",
159: "org.apache.jmeter.module.integration.HTMLEditorKit");
160: // ****
161:
162: PluginManager.install(this , true);
163:
164: modifiedListener.setActionHandler(ActionRouter.getInstance());
165: normalListener.setActionHandler(ActionRouter.getInstance());
166:
167: jmeterGui = GuiPackage.getInstance(modifiedListener, treeModel);
168: frame = new MainFrame(ActionRouter.getInstance(), treeModel,
169: modifiedListener);
170: jmeterGui.setMainFrame(frame);
171: ActionRouter.getInstance().addPostActionListener(Object.class,
172: new ActionListener() {
173: public void actionPerformed(ActionEvent e) {
174: // System.out.println("Event: " + e.getActionCommand());
175: }
176: });
177: // NB proprietary EditorKit hack
178: JTextPane.registerEditorKitForContentType("text/html",
179: editorClassBackup);
180: // ***
181:
182: treeModel.addTreeModelListener(new TreeModelListener() {
183: public void treeNodesChanged(TreeModelEvent e) {
184: // System.out.println("Nodes changed");
185: }
186:
187: public void treeNodesInserted(TreeModelEvent e) {
188: if (!loading) {
189: // System.out.println("Nodes inserted");
190: setDirty(true);
191: }
192: }
193:
194: public void treeNodesRemoved(TreeModelEvent e) {
195: setDirty(true);
196: }
197:
198: public void treeStructureChanged(TreeModelEvent e) {
199: // System.out.println("Tree changed");
200: }
201: });
202: }
203:
204: public final static JMeterGUISupport getDefault() {
205: if (instance == null) {
206: try {
207: initLock.acquire();
208: if (instance == null) {
209: instance = new JMeterGUISupport();
210: }
211: initLock.release();
212: } catch (InterruptedException e) {
213: }
214: }
215: return instance;
216: }
217:
218: // <editor-fold defaultstate="collapsed" desc="JMeterPlugin implementation" >
219: private static final String[][] DEFAULT_ICONS = {
220: { TestPlanGui.class.getName(),
221: "org/apache/jmeter/images/beaker.gif" },//$NON-NLS-1$
222: { AbstractTimerGui.class.getName(),
223: "org/apache/jmeter/images/timer.gif" },//$NON-NLS-1$
224: { ThreadGroupGui.class.getName(),
225: "org/apache/jmeter/images/thread.gif" },//$NON-NLS-1$
226: { AbstractVisualizer.class.getName(),
227: "org/apache/jmeter/images/meter.png" },//$NON-NLS-1$
228: { AbstractConfigGui.class.getName(),
229: "org/apache/jmeter/images/testtubes.png" },//$NON-NLS-1$
230: // Note: these were the original settings (just moved to a static
231: // array)
232: // Commented out because there is no such file
233: // {
234: // AbstractPreProcessorGui.class.getName(),
235: // "org/apache/jmeter/images/testtubes.gif" },
236: // {
237: // AbstractPostProcessorGui.class.getName(),
238: // "org/apache/jmeter/images/testtubes.gif" },
239: { AbstractControllerGui.class.getName(),
240: "org/apache/jmeter/images/knob.gif" },//$NON-NLS-1$
241: { WorkBenchGui.class.getName(),
242: "org/apache/jmeter/images/clipboard.gif" },//$NON-NLS-1$
243: { AbstractSamplerGui.class.getName(),
244: "org/apache/jmeter/images/pipet.png" } //$NON-NLS-1$
245: // AbstractAssertionGUI not defined
246: };
247:
248: public String[][] getIconMappings() {
249: String iconProp = JMeterUtils.getPropDefault("jmeter.icons",//$NON-NLS-1$
250: "org/apache/jmeter/images/icon.properties");//$NON-NLS-1$
251: Properties p = JMeterUtils.loadProperties(iconProp);
252: if (p == null) {
253: return DEFAULT_ICONS;
254: }
255: String[][] iconlist = new String[p.size()][3];
256: Enumeration pe = p.keys();
257: int i = 0;
258: while (pe.hasMoreElements()) {
259: String key = (String) pe.nextElement();
260: String icons[] = JOrphanUtils
261: .split(p.getProperty(key), " ");//$NON-NLS-1$
262: iconlist[i][0] = key;
263: iconlist[i][1] = icons[0];
264: if (icons.length > 1)
265: iconlist[i][2] = icons[1];
266: i++;
267: }
268: return iconlist;
269: }
270:
271: public String[][] getResourceBundles() {
272: return new String[0][];
273: }
274:
275: // </editor-fold>
276:
277: // <editor-fold defaultstate="collapsed" desc="Property change support">
278: public void addPropertyChangeListener(
279: final PropertyChangeListener pcl) {
280: pcs.addPropertyChangeListener(pcl);
281: }
282:
283: public void addPropertyChangeListener(final String propertyName,
284: final PropertyChangeListener pcl) {
285: pcs.addPropertyChangeListener(propertyName, pcl);
286: }
287:
288: public void removePropertyChangeListener(
289: final PropertyChangeListener pcl) {
290: pcs.removePropertyChangeListener(pcl);
291: }
292:
293: public void removePropertyChangeListener(final String propertyName,
294: final PropertyChangeListener pcl) {
295: pcs.removePropertyChangeListener(propertyName, pcl);
296: }
297:
298: // </editor-fold>
299:
300: public Component getGui(final TestElement element) {
301: JMeterGUIComponent gui = jmeterGui.getGui(element);
302: gui.configure(element);
303: return (Component) gui;
304: }
305:
306: public JPopupMenu getPopup(final TestElement element) {
307: currentNodeHolder.set(element);
308: return jmeterGui.getGui(element).createPopupMenu();
309: }
310:
311: public Image getIcon(final TestElement element) {
312: Object gui = jmeterGui.getGui(element);
313: ImageIcon icon = GUIFactory.getIcon(gui.getClass(), element
314: .isEnabled());
315: return icon != null ? icon.getImage() : null;
316: }
317:
318: public void loadScript(final FileObject script) {
319: try {
320: loading = true;
321: if (isDirty()) {
322: ActionRouter.getInstance().doActionNow(
323: new ActionEvent(this , 1, ActionNames.CLOSE));
324: }
325: HashTree tree = SaveService.loadTree(script
326: .getInputStream());
327: GuiPackage.getInstance().setTestPlanFile(
328: FileUtil.toFile(script).getAbsolutePath());
329:
330: new Load().insertLoadedTree(1, tree);
331: editing = true;
332: } catch (Exception e) {
333: e.printStackTrace();
334: } finally {
335: loading = false;
336: }
337: }
338:
339: public void unloadScript() {
340: GuiPackage.getInstance().getTreeModel().clearTestPlan();
341: GuiPackage.getInstance().getTreeListener().getJTree()
342: .setSelectionRow(1);
343:
344: // Clear the name of the test plan file
345: GuiPackage.getInstance().setTestPlanFile(null);
346:
347: ActionRouter.getInstance().actionPerformed(
348: new ActionEvent(this , 1, ActionNames.ADD_ALL));
349:
350: editing = false;
351: setDirty(false);
352: }
353:
354: public void saveScript(final FileObject script) throws Exception {
355: FileLock aLock = script.lock();
356: try {
357: OutputStreamWriter osw = new OutputStreamWriter(script
358: .getOutputStream(aLock));
359: HashTree plan = treeModel.getTestPlan();
360: convertSubTree(plan);
361: SaveService.saveTree(plan, osw);
362: ActionRouter.getInstance()
363: .doActionNow(
364: new ActionEvent(this , 1,
365: ActionNames.SUB_TREE_SAVED));
366: } finally {
367: aLock.releaseLock();
368: }
369: }
370:
371: public Container getEditor() {
372: return frame != null ? frame.getContentPane() : null;
373: }
374:
375: public void setDirty(final boolean value) {
376: if (value != modified) {
377: pcs.firePropertyChange(DIRTY, modified, value);
378: }
379: modified = value;
380: }
381:
382: public boolean isDirty() {
383: return modified;
384: }
385:
386: private void convertSubTree(HashTree tree) {
387: Iterator iter = new LinkedList(tree.list()).iterator();
388: while (iter.hasNext()) {
389: JMeterTreeNode item = (JMeterTreeNode) iter.next();
390: convertSubTree(tree.getTree(item));
391: TestElement testElement = item.getTestElement();
392: tree.replace(item, testElement);
393: }
394: }
395: }
|