View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
11   */
12  
13  package com.eviware.soapui;
14  
15  import java.awt.BorderLayout;
16  import java.awt.Color;
17  import java.awt.Component;
18  import java.awt.Dimension;
19  import java.awt.Insets;
20  import java.awt.Toolkit;
21  import java.awt.event.ActionEvent;
22  import java.awt.event.KeyEvent;
23  import java.awt.event.MouseAdapter;
24  import java.awt.event.MouseEvent;
25  import java.awt.event.WindowAdapter;
26  import java.awt.event.WindowEvent;
27  import java.io.File;
28  import java.io.IOException;
29  import java.net.URI;
30  import java.net.URISyntaxException;
31  import java.net.URL;
32  import java.util.ArrayList;
33  import java.util.Collection;
34  import java.util.Collections;
35  import java.util.List;
36  import java.util.Properties;
37  
38  import javax.swing.AbstractAction;
39  import javax.swing.Action;
40  import javax.swing.BorderFactory;
41  import javax.swing.ImageIcon;
42  import javax.swing.JComponent;
43  import javax.swing.JFrame;
44  import javax.swing.JLabel;
45  import javax.swing.JMenu;
46  import javax.swing.JMenuBar;
47  import javax.swing.JPanel;
48  import javax.swing.JScrollPane;
49  import javax.swing.JSplitPane;
50  import javax.swing.JTabbedPane;
51  import javax.swing.JWindow;
52  import javax.swing.SwingUtilities;
53  import javax.swing.ToolTipManager;
54  import javax.swing.UIManager;
55  import javax.swing.plaf.ColorUIResource;
56  import javax.xml.namespace.QName;
57  import javax.xml.parsers.FactoryConfigurationError;
58  
59  import org.apache.log4j.Level;
60  import org.apache.log4j.Logger;
61  import org.apache.log4j.xml.DOMConfigurator;
62  import org.apache.xmlbeans.XmlException;
63  
64  import com.eviware.soapui.actions.SoapUIPreferencesAction;
65  import com.eviware.soapui.config.SoapuiSettingsDocumentConfig;
66  import com.eviware.soapui.impl.settings.XmlBeansSettingsImpl;
67  import com.eviware.soapui.impl.wsdl.actions.iface.tools.axis1.Axis1XWSDL2JavaAction;
68  import com.eviware.soapui.impl.wsdl.actions.iface.tools.axis2.Axis2WSDL2CodeAction;
69  import com.eviware.soapui.impl.wsdl.actions.iface.tools.dotnet.DotNetWsdlAction;
70  import com.eviware.soapui.impl.wsdl.actions.iface.tools.gsoap.GSoapAction;
71  import com.eviware.soapui.impl.wsdl.actions.iface.tools.jaxb.JaxbXjcAction;
72  import com.eviware.soapui.impl.wsdl.actions.iface.tools.jbossws.JBossWSConsumeAction;
73  import com.eviware.soapui.impl.wsdl.actions.iface.tools.jbossws.WSToolsWsdl2JavaAction;
74  import com.eviware.soapui.impl.wsdl.actions.iface.tools.oracle.OracleWsaGenProxyAction;
75  import com.eviware.soapui.impl.wsdl.actions.iface.tools.support.SwingToolHost;
76  import com.eviware.soapui.impl.wsdl.actions.iface.tools.tcpmon.TcpMonAction;
77  import com.eviware.soapui.impl.wsdl.actions.iface.tools.wscompile.WSCompileAction;
78  import com.eviware.soapui.impl.wsdl.actions.iface.tools.wsimport.WSImportAction;
79  import com.eviware.soapui.impl.wsdl.actions.iface.tools.xfire.XFireAction;
80  import com.eviware.soapui.impl.wsdl.actions.iface.tools.xmlbeans.XmlBeans2Action;
81  import com.eviware.soapui.impl.wsdl.actions.support.OpenUrlAction;
82  import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
83  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
84  import com.eviware.soapui.model.ModelItem;
85  import com.eviware.soapui.model.PanelBuilder;
86  import com.eviware.soapui.model.settings.Settings;
87  import com.eviware.soapui.model.settings.SettingsListener;
88  import com.eviware.soapui.model.tree.SoapUITreeNode;
89  import com.eviware.soapui.model.util.PanelBuilderRegistry;
90  import com.eviware.soapui.model.workspace.Workspace;
91  import com.eviware.soapui.model.workspace.WorkspaceFactory;
92  import com.eviware.soapui.monitor.MockEngine;
93  import com.eviware.soapui.monitor.TestMonitor;
94  import com.eviware.soapui.settings.HttpSettings;
95  import com.eviware.soapui.settings.UISettings;
96  import com.eviware.soapui.settings.WsdlSettings;
97  import com.eviware.soapui.support.ClasspathHacker;
98  import com.eviware.soapui.support.SoapUIException;
99  import com.eviware.soapui.support.UISupport;
100 import com.eviware.soapui.support.action.ActionSupport;
101 import com.eviware.soapui.support.log.Log4JMonitor;
102 import com.eviware.soapui.support.log.LogDisablingTestMonitorListener;
103 import com.eviware.soapui.support.types.StringList;
104 import com.eviware.soapui.ui.JDesktopPanelsList;
105 import com.eviware.soapui.ui.Navigator;
106 import com.eviware.soapui.ui.NavigatorListener;
107 import com.eviware.soapui.ui.desktop.DesktopPanel;
108 import com.eviware.soapui.ui.desktop.DesktopRegistry;
109 import com.eviware.soapui.ui.desktop.SoapUIDesktop;
110 import com.eviware.soapui.ui.desktop.standalone.StandaloneDesktop;
111 import com.eviware.soapui.ui.desktop.standalone.StandaloneDesktopFactory;
112 import com.eviware.soapui.ui.support.DesktopListenerAdapter;
113 import com.eviware.x.form.XFormFactory;
114 import com.eviware.x.impl.swing.SwingFormFactory;
115 import com.jgoodies.looks.HeaderStyle;
116 import com.jgoodies.looks.Options;
117 import com.jgoodies.looks.plastic.PlasticXPLookAndFeel;
118 import com.jgoodies.looks.plastic.theme.SkyBluer;
119 
120 /***
121  * Main SoapUI entry point.
122  * 
123  * @author Ole.Matzura
124  * 
125  * For version 0.6
126  * @todo improve error assertion reporting +
127  * @todo find/search/replace +
128  * @todo undo +
129  * @todo recreate request and keep values +
130  * @todo dont create optional elements +
131  * @todo option to create requests when importing +
132  * @todo show soapaction +
133  * @todo save response +
134  * @todo color-coding +
135  * @todo rapportera fel vid null-svar +
136  * @todo fixa bugg vid update interface +
137  * @todo xmlbeans2 +
138  * @todo httpclient rc3 +
139  * @todo refactor request panels +
140  * @todo refactor project/interface/testsuite listeners +
141  * @todo create properties table +
142  * 
143  * For version 0.7
144  * @todo workspace in home folder +
145  * @todo allow renaming of operation names in tree +
146  * @todo add functionality for copying values from test response to following
147  *       test request +
148  * @todo fix relative xsd imports when loading from file system +
149  * @todo fix cancelling of request when closing request window -> introduce
150  *       DesktopPanel interface +
151  * @todo refactor panel creation / listener handling/release +
152  * @todo fix update of testcase window when adding test steps +
153  * @todo add delete-line action in xml-editor (Ctrl-D) +
154  * @todo add submit action to xml request editor (Ctrl Enter) +
155  * @todo add mousewheel support to xml editor +
156  * @todo add mousewheel + C support to xml editor (move cursor) +
157  * @todo fix revalidation problems +
158  * @todo add validation to xmleditors +
159  * @todo check localisation of schema errors +
160  * @todo add possibility to reload schema (neccessary?) +
161  * @todo fix cloning of assertions when cloning test steps +
162  * @todo ask for creation of optional elements when (re)creating new request +
163  * @todo add action to add existing namespaces to xpath in xpathassertion +
164  * 
165  * For version 0.8
166  * @todo fix default values when creating requests +
167  * @todo bug-fixes - creation of namespace declarations creates multiple with
168  *       same name + - check defintion update + - deletion of endpoint always
169  *       deletes first + - deleted assertions result in error during save/exit? + -
170  *       disable submit when missing endpoint + - clone request null-pointer + -
171  *       fix value transfer element <> attribute + - fix cancelling of test
172  *       requests +
173  * @todo create assertion events / listeners +
174  * @todo ask to add testcase if missing +
175  * @todo add alt-enter to response area +
176  * @todo allow url config of SchemaCompliance assertion +
177  * @todo add accelerators for common actions +
178  * @todo fix positions of dialogs +
179  * @todo add possibility to specify testsuite/testcase in maven-plugin +
180  * @todo refactoring: replaced SoapUIAction with standard swing Action +
181  * @todo refactoring: checked all JOptionPanes for correct title, etc. +
182  * @todo add filename-filters to filechoosers +
183  * @todo add shortcut for cancel request to editor +
184  * 
185  * For version 0.9
186  * @todo initial class/code comments +
187  * @todo bug-fixes - fix cancelling of test runs + - fix removal of request
188  *       test-steps when associated operations / interfaces are removed + -
189  *       update of request-step status in testcase panel + - focus on find-field
190  *       in find/replace dialog + - fix filters to not remove directories.. + -
191  *       change wsdl file filter to *.wsdl + - disable declare in transfer panel
192  *       when no transfer is selected + - declare in transfer panel declares
193  *       target from target response (should be request) + - ask to cancel when
194  *       closing running testcase + - fix pretty-printing of large xml-documents + -
195  *       fix find and replace + - fix node insert error on multiple clone
196  *       teststep + - fix log-messages from canceled requests + - disable
197  *       replace-related options in find/replace if editor is not editable +
198  * @todo add possibility to move teststeps within testcase +
199  * @todo improve/disable disabling of request-editor +
200  * @todo add addAssertion button to testrequest panel +
201  * @todo add copy/pase actions to editor popup +
202  * @todo model-items tests
203  * @todo improve wsdl/xsd viewer +
204  * @todo improve line-number reporting in validations +
205  * @todo added shift-tab to move focus between request/response editors +
206  * @todo refactorings - SoapUIException + - UIFacade for JOptionPane +
207  *       (partially)
208  * 
209  * For version 1.0b
210  * @todo initial user documentation / samples +
211  * @todo bug-fixes +
212  * @todo more tests / testing!
213  * @todo beta release +
214  * 
215  * For version 1.0
216  * @todo official release
217  * 
218  * For version 1.5
219  * @todo finish LoadTest panels - fix UI + - color legend + - reset action + -
220  *       interactive thread-count change + - error-log + - see responses + -
221  *       assertions + - command-line/maven-plugin + - export results +
222  * @todo loadtest fixes - resizing/scaling of graphs + - autoreset on restart +
223  * @todo fix disabling of relevant actions during test-runs + - Add to testcase,
224  *       delete testcase, etc +
225  * @todo allow TransferValues between any requests/responses +
226  * @todo fix "global values" in testcases + - properties +
227  * @todo improve endpoint editing + - add "edit.." to combobox +
228  * @todo remove xpath/xquery tester.. +
229  * @todo improve testcase properties dialog +
230  * @todo fix icons for all tree nodes +
231  * @todo fix updating of desktop panel titles +
232  * @todo global settings; + - proxy + - close connections + - user-agent header + -
233  *       preemptive authentication + - editor font + - socket timeout + - ?
234  * @todo add loop-functionality for testcases +
235  * @todo support one-way operations +
236  * @todo fix wsdlcontext initialization in loadtests +
237  * @todo create testrun summary in commandline
238  * @todo create loadtest summary in commandline
239  * @todo add maven.soapui.test.host commandline property +
240  * @todo add test-suite runner panel +
241  * @todo option to abort testcase on error +
242  * @todo ensure unique test-step names +
243  * @todo grooy-teststep +
244  * @todo onewayop tree icon +
245  * @todo fix desktoppanels list +
246  * 
247  * For version 1.6
248  * @todo finish SOAP 1.2 request generation +
249  * @todo finish gsoap integration +
250  * @todo bugfixes!
251  * @todo swa-attachments support +
252  * 
253  * Improvements
254  * @todo make modifications of response content "alive"
255  * @todo animated tree icons for testcase/loadtest runs
256  * @todo add run-testcase action in teststep/testcase editors
257  * @todo implement testsuite/testcase generation
258  * @todo refactor XmlObject configurables
259  * @todo workspace management
260  * @todo add reload-project!?
261  * @todo option to reload schemas on run testcase
262  * @todo option to select visible steps in loadtest statistics
263  * @todo generate testcase/testsuite actions
264  * @todo form-based input
265  */
266 
267 public class SoapUI
268 {
269 	public final static Logger log = Logger.getLogger( SoapUI.class );
270 	public final static String SOAPUI_VERSION = "1.7.1";
271 	public static final String DEFAULT_WORKSPACE_FILE = "default-soapui-workspace.xml";
272 	private final static String DEFAULT_SETTINGS_FILE = "soapui-settings.xml";
273 	public static final String SOAPUI_SPLASH_GIF = "soapui-splash.gif";
274 
275 	// ------------------------------ FIELDS ------------------------------
276 
277 	private static SoapUI instance;
278 	private static SoapuiSettingsDocumentConfig settingsDocument;
279 	private static List<Object> logCache = new ArrayList<Object>();
280 	private static Settings settings;
281 
282 	private static JFrame frame;
283 
284 	private Navigator navigator;
285 	private SoapUIDesktop desktop;
286 	private static TestMonitor testMonitor;
287 	private Workspace workspace;
288 	private JDesktopPanelsList desktopPanelsList;
289 	private Log4JMonitor logMonitor;
290 
291 	private JMenu desktopMenu;
292 	private JMenu fileMenu;
293 	private JMenuBar menuBar;
294 
295 	public static boolean checkedGroovyLogMonitor;
296 	
297 	private JPanel overviewPanel;
298 	private JMenu toolsMenu;
299 	private boolean saveOnExit = true;
300 	private static MockEngine mockEngine;
301 	private static boolean loadedExtLibs;
302 	private static boolean isStandalone;
303 	private JSplitPane contentSplit;
304 	private InternalDesktopListener internalDesktopListener = new InternalDesktopListener();
305 
306 	// --------------------------- CONSTRUCTORS ---------------------------
307 
308 	public SoapUI()
309 	{
310 		instance = this;
311 		testMonitor = new TestMonitor();
312 	}
313 
314 	private void buildUI()
315 	{
316 		frame.addWindowListener( new MainFrameWindowListener() );
317 		UISupport.setMainFrame( frame );
318 
319 		navigator = new Navigator( workspace );
320 		navigator.addNavigatorListener( new InternalNavigatorListener() );
321 
322 		desktopPanelsList = new JDesktopPanelsList( desktop );
323 
324 		JSplitPane splitPane = UISupport
325 					.createHorizontalSplit( buildMainPanel(), buildContentPanel() );
326 
327 		frame.setJMenuBar( buildMainMenu() );
328 		frame.getContentPane().add( splitPane, BorderLayout.CENTER );
329 		frame.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
330 		frame.setSize( 1000, 750 );
331 
332 		splitPane.setDividerLocation( 250 );
333 
334 		navigator.selectModelItem( workspace );
335 
336 		desktop.addDesktopListener( internalDesktopListener );
337 
338 		ToolTipManager.sharedInstance().setInitialDelay( 200 );
339 	}
340 
341 	private JMenuBar buildMainMenu()
342 	{
343 		menuBar = new JMenuBar();
344 		menuBar.putClientProperty( Options.HEADER_STYLE_KEY, HeaderStyle.BOTH );
345 
346 		menuBar.add( buildFileMenu() );
347 		menuBar.add( buildToolsMenu() );
348 		menuBar.add( buildDesktopMenu() );
349 		menuBar.add( buildHelpMenu() );
350 
351 		return menuBar;
352 	}
353 
354 	private JMenu buildDesktopMenu()
355 	{
356 		desktopMenu = new JMenu( "Desktop" );
357 		desktopMenu.setMnemonic( KeyEvent.VK_D );
358 
359 		ActionSupport.addActions( desktop.getActions(), desktopMenu );
360 
361 		return desktopMenu;
362 	}
363 
364 	private JMenu buildHelpMenu()
365 	{
366 		desktopMenu = new JMenu( "Help" );
367 		desktopMenu.setMnemonic( KeyEvent.VK_H );
368 
369 		desktopMenu.add( new ShowOnlineHelpAction( "User Guide", HelpUrls.USERGUIDE_HELP_URL ) );
370 		desktopMenu.add( new ShowOnlineHelpAction( "Getting Started",
371 					HelpUrls.GETTINGSTARTED_HELP_URL ) );
372 		desktopMenu.addSeparator();
373 		
374 		desktopMenu.add( new ShowSystemPropertiesAction());
375 		
376 		desktopMenu.addSeparator();
377 		desktopMenu.add( new OpenUrlAction( "soapui.org", "http://www.soapui.org" ) );
378 		desktopMenu.add( new AboutAction() );
379 		return desktopMenu;
380 	}
381 
382 	private JMenu buildToolsMenu()
383 	{
384 		toolsMenu = new JMenu( "Tools" );
385 		toolsMenu.setMnemonic( KeyEvent.VK_T );
386 
387 		toolsMenu.add( new WSToolsWsdl2JavaAction( null ) );
388 		toolsMenu.add( new JBossWSConsumeAction( null ));
389 		toolsMenu.addSeparator();
390 		toolsMenu.add( new WSCompileAction( null ) );
391 		toolsMenu.add( new WSImportAction( null ) );
392 		toolsMenu.addSeparator();
393 		toolsMenu.add( new Axis1XWSDL2JavaAction( null ) );
394 		toolsMenu.add( new Axis2WSDL2CodeAction( null ) );
395 		toolsMenu.add( new XFireAction( null ) );
396 		toolsMenu.add( new OracleWsaGenProxyAction( null ) );
397 		toolsMenu.addSeparator();
398 		toolsMenu.add( new XmlBeans2Action( null ) );
399 		toolsMenu.add( new JaxbXjcAction( null ) );
400 		toolsMenu.addSeparator();
401 		toolsMenu.add( new DotNetWsdlAction( null ) );
402 		toolsMenu.add( new GSoapAction( null ) );
403 		toolsMenu.addSeparator();
404 		toolsMenu.add( new TcpMonAction( null ) );
405 		// toolsMenu.addSeparator();
406 		// toolsMenu.add( new XQueryXPathTesterAction());
407 
408 		return toolsMenu;
409 	}
410 
411 	private JMenu buildFileMenu()
412 	{
413 		fileMenu = new JMenu( "File" );
414 		fileMenu.setMnemonic( KeyEvent.VK_F );
415 
416 		ActionSupport.addActions( workspace.getActions(), fileMenu );
417 
418 		fileMenu.addSeparator();
419 		fileMenu.add( new SoapUIPreferencesAction() );
420 		fileMenu.add( new SavePreferencesAction() );
421 
422 		fileMenu.addSeparator();
423 		fileMenu.add( new ExitAction() );
424 		fileMenu.add( new ExitWithoutSavingAction() );
425 
426 		return fileMenu;
427 	}
428 
429 	public JFrame getFrame()
430 	{
431 		return frame;
432 	}
433 
434 	private JComponent buildMainPanel()
435 	{
436 		JSplitPane splitPane = UISupport.createVerticalSplit();
437 
438 		splitPane.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );
439 
440 		splitPane.setTopComponent( navigator );
441 		splitPane.setBottomComponent( buildOverviewPanel() );
442 		splitPane.setDividerLocation( 500 );
443 		splitPane.setResizeWeight( 0.6 );
444 		return splitPane;
445 	}
446 
447 	private Component buildOverviewPanel()
448 	{
449 		JTabbedPane detailTabs = new JTabbedPane( JTabbedPane.BOTTOM );
450 
451 		overviewPanel = new JPanel( new BorderLayout() );
452 		detailTabs.addTab( "Details", overviewPanel );
453 		detailTabs.addTab( "Windows", new JScrollPane( desktopPanelsList ) );
454 
455 		return UISupport.createTabPanel( detailTabs, true );
456 	}
457 
458 	private void setOverviewPanel( Component panel )
459 	{
460 		if( overviewPanel.getComponentCount() == 0 && panel == null )
461 			return;
462 
463 		overviewPanel.removeAll();
464 		if( panel != null )
465 			overviewPanel.add( panel, BorderLayout.CENTER );
466 		overviewPanel.revalidate();
467 		overviewPanel.repaint();
468 	}
469 
470 	private Component buildContentPanel()
471 	{
472 		contentSplit = UISupport.createVerticalSplit();
473 		contentSplit.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );
474 
475 		contentSplit.setTopComponent( desktop.getDesktopComponent() );
476 		contentSplit.setBottomComponent( buildLogPanel( true, "soapUI log" ) );
477 		contentSplit.setDividerLocation( 550 );
478 		contentSplit.setResizeWeight( 1 );
479 		return contentSplit;
480 	}
481 
482 	public Component buildLogPanel( boolean hasDefault, String defaultName )
483 	{
484 		logMonitor = new Log4JMonitor();
485 		logMonitor.addLogArea( defaultName, "com.eviware.soapui", hasDefault ).setLevel( Level.DEBUG );
486 		logMonitor.addLogArea( "http log", "httpclient.wire", false ).setLevel( Level.DEBUG );
487 		logMonitor.addLogArea( "jetty log", "jetty", false ).setLevel( Level.INFO );
488 
489 		for( Object message : logCache )
490 		{
491 			logMonitor.logEvent( message );
492 		}
493 
494 		return UISupport.createTabPanel( logMonitor, true );
495 	}
496 
497 	// -------------------------- OTHER METHODS --------------------------
498 
499 	public static synchronized void log( final Object msg )
500 	{
501 		if( instance == null || instance.logMonitor == null )
502 		{
503 			logCache.add( msg );
504 			return;
505 		}
506 
507 		if( SwingUtilities.isEventDispatchThread() )
508 		{
509 			instance.logMonitor.logEvent( msg );
510 		}
511 		else
512 		{
513 			SwingUtilities.invokeLater( new Runnable()
514 			{
515 				public void run()
516 				{
517 					instance.logMonitor.logEvent( msg );
518 				}
519 			} );
520 		}
521 	}
522 
523 	// -------------------------- INNER CLASSES --------------------------
524 
525 	private final class InternalDesktopListener extends DesktopListenerAdapter
526 	{
527 		public void desktopPanelSelected( DesktopPanel desktopPanel )
528 		{
529 			ModelItem modelItem = desktopPanel.getModelItem();
530 			if( modelItem != null )
531 				navigator.selectModelItem( modelItem );
532 		}
533 	}
534 
535 	private final class MainFrameWindowListener extends WindowAdapter
536 	{
537 		public void windowClosing( WindowEvent e )
538 		{
539 			if( saveOnExit )
540 			{
541 				if( !UISupport.confirm( "Exit SoapUI?", "Question" ) )
542 					return;
543 
544 				workspace.onClose();
545 
546 				try
547 				{
548 					saveSettings();
549 				}
550 				catch( IOException e1 )
551 				{
552 					e1.printStackTrace();
553 				}
554 			}
555 			else
556 			{
557 				if( !UISupport.confirm( "Exit SoapUI without saving?", "Question" ) )
558 				{
559 					saveOnExit = true;
560 					return;
561 				}
562 			}
563 
564 			frame.dispose();
565 		}
566 
567 		public void windowClosed( WindowEvent e )
568 		{
569 			System.out.println( "exiting.." );
570 			System.exit( 0 );
571 		}
572 	}
573 
574 	public static class SoapUITheme extends SkyBluer
575 	{
576 		public static final Color BACKGROUND_COLOR = new Color( 240, 240, 240 );
577 
578 		public ColorUIResource getControl()
579 		{
580 			return new ColorUIResource( BACKGROUND_COLOR );
581 		}
582 
583 		public ColorUIResource getMenuBackground()
584 		{
585 			return getControl();
586 		}
587 
588 		public ColorUIResource getMenuItemBackground()
589 		{
590 			return new ColorUIResource( new Color( 248, 248, 248 ) );
591 		}
592 	}
593 
594 	// --------------------------- main() method ---------------------------
595 
596 	public static void main( String[] args ) throws Exception
597 	{
598 		String title = "soapUI " + SOAPUI_VERSION;
599 		String splashImage = "soapui-splash.gif";
600 		
601 		startSoapUI( args, title, findSplash( splashImage ));
602 	}
603 
604 	public static URL findSplash( String filename )
605 	{
606 		File file = new File( filename );
607 		URL url = null;
608 		
609 		try
610 		{
611 			if( !file.exists() )
612 				url = SoapUI.class.getResource( "/" + filename );
613 			else
614 				url = file.toURL();
615 		}
616 		catch( Exception e1 )
617 		{
618 		}
619 		
620 		try
621 		{
622 			if( url == null )
623 				url = new URL( "http://www.soapui.org/images/" + filename );
624 		}
625 		catch( Exception e2 )
626 		{
627 			e2.printStackTrace();
628 		}
629 		
630 		return url;
631 	}
632 	
633 	public static void startSoapUI( String[] args, String title, URL splashImage ) throws FactoryConfigurationError, SoapUIException
634 	{
635 		isStandalone = true;
636 		
637 		initSoapUILog();
638 		
639 		frame = new JFrame( title );
640 		new SoapUISplash( splashImage );
641 		
642 		initSoapUILookAndFeel();
643 		prepareSwingUI();
644 		loadExtLibs();
645 		
646 		DesktopRegistry.getInstance().addDesktop( "Default", new StandaloneDesktopFactory() );
647 		
648 		// Run with the currently installed license.
649 		SoapUI soapUI = new SoapUI();
650 
651 		Workspace workspace = null;
652 
653 		if( args.length > 0 )
654 			workspace = WorkspaceFactory.getInstance().openWorkspace( args );
655 		else
656 			workspace = WorkspaceFactory.getInstance().openWorkspace(
657 						new String[] { System.getProperty( "user.home" ) + File.separatorChar
658 									+ DEFAULT_WORKSPACE_FILE } );
659 		
660 		soapUI.show( workspace );
661 	}
662 	
663 	public static boolean isStandalone()
664 	{
665 		return isStandalone;
666 	}
667 
668 	public static void loadExtLibs()
669 	{
670 		if( loadedExtLibs )
671 			return;
672 		
673 		try
674 		{
675 			File dir = new File( "ext" );
676 			
677 			if( dir.exists() && dir.isDirectory() )
678 			{
679 				File[] files = dir.listFiles();
680 				for( File file : files )
681 				{
682 					if( file.getName().toLowerCase().endsWith( ".jar" ))
683 					{
684 						ClasspathHacker.addFile( file );
685 					}
686 				}
687 			}
688 			else
689 			{
690 				log.info( "Missing folder [" + dir.getAbsolutePath() + "] for external libraries" );
691 			}
692 			
693 			loadedExtLibs = true;
694 		}
695 		catch( IOException e )
696 		{
697 			e.printStackTrace();
698 		}
699 	}
700 
701 	public static void prepareSwingUI()
702 	{
703 		UISupport.setToolHost( new SwingToolHost() );
704       XFormFactory.Factory.instance = new SwingFormFactory();
705 	}
706 
707 	public static void initSoapUILookAndFeel()
708 	{
709 		try
710 		{
711 			if( getSettings().getBoolean( UISettings.NATIVE_LAF ))
712 			{
713 				javax.swing.UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
714 			}
715 			else
716 			{
717 				SoapUITheme theme = new SoapUITheme();
718 
719 				PlasticXPLookAndFeel.setCurrentTheme( theme );
720 				PlasticXPLookAndFeel.setTabStyle( "Metal" );
721 
722 				UIManager.setLookAndFeel( new PlasticXPLookAndFeel() );
723 				UIManager.put( "TabbedPane.tabAreaInsets", new Insets( 3, 2, 0, 0 ) );
724 				UIManager.put( "TabbedPane.unselectedBackground", new Color( 220, 220, 220 ) );
725 				UIManager.put( "TabbedPane.selected", new Color( 240, 240, 240 ) );
726 				
727 				PlasticXPLookAndFeel.setPlasticTheme( theme );
728 			}
729 		}
730 		catch( Throwable e )
731 		{
732 			System.err.println( "Error initializing PlasticXPLookAndFeel; " + e.getMessage() );
733 		}
734 	}
735 
736 	public static JMenuBar getMenuBar()
737 	{
738 		return instance == null ? null : instance.menuBar;
739 	}
740 	
741 	public static void initSoapUILog() throws FactoryConfigurationError
742 	{
743 		File log4jconfig = new File( "soapui-log4j.xml" );
744 		if( log4jconfig.exists() )
745 		{
746 			System.out.println( "Configuring log4j from [" + log4jconfig + "]" );
747 			DOMConfigurator.configureAndWatch( log4jconfig.getAbsolutePath(), 5000 );
748 		}
749 		else
750 		{
751 			URL log4jUrl = SoapUI.class.getResource( "/soapui-log4j.xml" );
752 			System.out.println( "Configuring log4j from [" + log4jUrl + "]" );
753 			DOMConfigurator.configure( log4jUrl );
754 		}
755 	}
756 
757 	private void show( Workspace workspace )
758 	{
759 		this.workspace = workspace;
760 
761 		desktop = new StandaloneDesktop( workspace );
762 
763 		String desktopType = getSettings().getString( UISettings.DESKTOP_TYPE, "Default");
764 		desktop = DesktopRegistry.getInstance().createDesktop( desktopType, workspace );
765 		
766 		if( desktop == null ) 
767 			desktop = new StandaloneDesktop( workspace );
768 
769 		getSettings().addSettingsListener( new SettingsListener() {
770 
771 			public void settingChanged( String name, String newValue, String oldValue )
772 			{
773 				if( name.equals( UISettings.DESKTOP_TYPE ))
774 				{
775 					changeDesktop( DesktopRegistry.getInstance().createDesktop( newValue, SoapUI.this.workspace ));
776 				}
777 			}
778 		} );
779 		
780 		buildUI();
781 		testMonitor.addTestMonitorListener( new LogDisablingTestMonitorListener() );
782 		testMonitor.init( workspace );
783 		frame.setVisible( true );
784 	}
785 	
786 	private void changeDesktop(SoapUIDesktop newDesktop )
787 	{
788 		desktopPanelsList.setDesktop( newDesktop );
789 		desktop.removeDesktopListener( internalDesktopListener );
790 		
791 		desktop.transferTo( newDesktop );
792 		
793 		desktop = newDesktop;
794 		contentSplit.setTopComponent( desktop.getDesktopComponent() );
795 		
796 		desktop.addDesktopListener( internalDesktopListener );
797 		
798 		desktopMenu.removeAll();
799 		ActionSupport.addActions( desktop.getActions(), desktopMenu );
800 	}
801 
802 	public static synchronized Logger ensureGroovyLog()
803 	{
804 		if( !checkedGroovyLogMonitor )
805 		{
806 			Log4JMonitor logMonitor = getLogMonitor();
807 			if( logMonitor != null && !logMonitor.hasLogArea( "groovy.log" ))
808 				logMonitor.addLogArea( "groovy log", "groovy.log", false );
809 			
810 			checkedGroovyLogMonitor = true;
811 		}
812 		
813 		return Logger.getLogger( "groovy.log" );
814 	}
815 
816 	public class InternalNavigatorListener implements NavigatorListener
817 	{
818 		public void nodeSelected( SoapUITreeNode treeNode )
819 		{
820 			if( treeNode == null )
821 			{
822 				setOverviewPanel( null );
823 			}
824 			else
825 			{
826 				PanelBuilder<ModelItem> panelBuilder = PanelBuilderRegistry.getPanelBuilder( treeNode.getModelItem() );
827 				if( panelBuilder != null && panelBuilder.hasOverviewPanel() )
828 				{
829 					setOverviewPanel( panelBuilder.buildOverviewPanel( treeNode.getModelItem() ) );
830 				}
831 				else
832 				{
833 					setOverviewPanel( null );
834 				}
835 			}
836 		}
837 	}
838 
839 	private class ExitAction extends AbstractAction
840 	{
841 		public ExitAction()
842 		{
843 			super( "Exit" );
844 			putValue( Action.SHORT_DESCRIPTION, "Saves all projects and exits SoapUI" );
845 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "menu Q" ) );
846 		}
847 
848 		public void actionPerformed( ActionEvent e )
849 		{
850 			saveOnExit = true;
851 			WindowEvent windowEvent = new WindowEvent( frame, WindowEvent.WINDOW_CLOSING );
852 			frame.dispatchEvent( windowEvent );
853 		}
854 	}
855 
856 	private class ShowSystemPropertiesAction extends AbstractAction
857 	{
858 		public ShowSystemPropertiesAction()
859 		{
860 			super( "System Properties" );
861 			putValue( Action.SHORT_DESCRIPTION, "Shows the current systems properties" );
862 		}
863 
864 		public void actionPerformed( ActionEvent e )
865 		{
866 			StringBuffer buffer = new StringBuffer();
867 			Properties properties = System.getProperties();
868 			
869 			List<String> keys = new ArrayList<String>();
870 			for( Object key : properties.keySet() )
871 				keys.add( key.toString() );
872 			
873 			Collections.sort( keys );
874 			
875 			String lastKey = null;
876 			
877 			for( String key : keys )
878 			{
879 				if( lastKey != null )
880 				{
881 					if( !key.startsWith( lastKey ))
882 						buffer.append(  "\r\n" );
883 				}
884 
885 				int ix = key.indexOf( '.' );
886 				lastKey = ix == -1 ? key : key.substring( 0, ix );
887 				
888 				buffer.append( key ).append( '=' ).append( properties.get( key )).append( "\r\n" );
889 			}
890 			
891 			UISupport.showExtendedInfo( "System Properties", "Current system properties", 
892 						"<html><body><pre><font size=-1>" + buffer.toString() + "</font></pre></body></html>", 
893 						new Dimension( 600, 400 ));
894 		}
895 	}
896 	
897 	private class AboutAction extends AbstractAction
898 	{
899 		public AboutAction()
900 		{
901 			super( "About soapUI" );
902 			putValue( Action.SHORT_DESCRIPTION, "Shows information on soapUI" );
903 		}
904 
905 		public void actionPerformed( ActionEvent e )
906 		{
907 			URI splashURI = null;
908 			try
909 			{
910 				splashURI = SoapUI.findSplash( SoapUI.SOAPUI_SPLASH_GIF ).toURI();
911 			}
912 			catch( URISyntaxException e1 )
913 			{
914 				e1.printStackTrace();
915 			}
916 			
917 			Properties props = new Properties();
918 			try
919 			{
920 				props.load( SoapUI.class.getResourceAsStream( "/buildinfo.txt" ) );
921 			}
922 			catch( Exception e1 )
923 			{
924 				e1.printStackTrace();
925 			}
926 			
927 			
928 			UISupport.showExtendedInfo( "About soapUI", null, 
929 						"<html><body><p align=center><img src=\"" + splashURI + "\"><br>soapUI " +
930 						SOAPUI_VERSION + ", copyright (C) 2004-2007 eviware.com<br>" +
931 						"<a href=\"http://www.soapui.org\">http://www.soapui.org</a> | " +
932 						"<a href=\"http://www.eviware.com\">http://www.eviware.com</a><br>" +
933 									"Build " + props.getProperty( "build.number" ) + ", Build Date " +
934 									props.getProperty( "build.date" ) + "</p></body></html>",
935 									
936 						new Dimension( 470, 350 ));
937 		}
938 	}
939 	
940 	
941 	private class ExitWithoutSavingAction extends AbstractAction
942 	{
943 		public ExitWithoutSavingAction()
944 		{
945 			super( "Exit without saving" );
946 			putValue( Action.SHORT_DESCRIPTION, "Saves all projects and exits SoapUI" );
947 			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "ctrl alt Q" ) );
948 		}
949 
950 		public void actionPerformed( ActionEvent e )
951 		{
952 			saveOnExit = false;
953 			WindowEvent windowEvent = new WindowEvent( frame, WindowEvent.WINDOW_CLOSING );
954 			frame.dispatchEvent( windowEvent );
955 		}
956 	}
957 
958 	private class SavePreferencesAction extends AbstractAction
959 	{
960 		public SavePreferencesAction()
961 		{
962 			super( "Save Preferences" );
963 			putValue( Action.SHORT_DESCRIPTION, "Saves all global preferences" );
964 		}
965 
966 		public void actionPerformed( ActionEvent e )
967 		{
968 			try
969 			{
970 				saveSettings();
971 			}
972 			catch( IOException e1 )
973 			{
974 				UISupport.showErrorMessage( e1 );
975 			}
976 		}
977 	}
978 
979 	public static TestMonitor getTestMonitor()
980 	{
981 		return testMonitor;
982 	}
983 
984 	public static void setTestMonitor( TestMonitor monitor )
985 	{
986 		testMonitor = monitor;
987 	}
988 
989 	public static Log4JMonitor getLogMonitor()
990 	{
991 		return instance == null ? null : instance.logMonitor;
992 	}
993 
994 	public static void setLogMonitor( Log4JMonitor monitor )
995 	{
996 		if( instance != null )
997 			instance.logMonitor = monitor;
998 	}
999 
1000 	public static Settings getSettings()
1001 	{
1002 		if( settings == null )
1003 		{
1004 			if( settingsDocument == null )
1005 				initSettings( DEFAULT_SETTINGS_FILE );
1006 
1007 			if( settingsDocument.getSoapuiSettings() == null )
1008 			{
1009 				settingsDocument.addNewSoapuiSettings();
1010 				settings = new XmlBeansSettingsImpl( null, null, settingsDocument.getSoapuiSettings() );
1011 
1012 				settings.setBoolean( WsdlSettings.CACHE_WSDLS, true );
1013 				settings.setBoolean( WsdlSettings.PRETTY_PRINT_RESPONSE_MESSAGES, true );
1014 				
1015 				settings.setBoolean( HttpSettings.INCLUDE_REQUEST_IN_TIME_TAKEN, true );
1016 				settings.setBoolean( HttpSettings.INCLUDE_RESPONSE_IN_TIME_TAKEN, true );
1017 				
1018 			}
1019 			else
1020 			{
1021 				settings = new XmlBeansSettingsImpl( null, null, settingsDocument.getSoapuiSettings() );
1022 			}
1023 			
1024 			if( !settings.isSet( WsdlSettings.EXCLUDED_TYPES ))
1025 			{
1026 				StringList list = new StringList();
1027 				list.add( "schema@http://www.w3.org/2001/XMLSchema");
1028 				settings.setString( WsdlSettings.EXCLUDED_TYPES, list.toXml() );
1029 			}
1030 
1031 			if( !settings.isSet( WsdlSettings.NAME_WITH_BINDING ))
1032 			{
1033 				settings.setBoolean( WsdlSettings.NAME_WITH_BINDING, true );
1034 			}
1035 		}
1036 
1037 		return settings;
1038 	}
1039 
1040 	public static void saveSettings() throws IOException
1041 	{
1042 		settingsDocument.save( new File( DEFAULT_SETTINGS_FILE ) );
1043 		log.info( "Saved global preferences to [" + DEFAULT_SETTINGS_FILE + "]" );
1044 	}
1045 
1046 	/***
1047 	 * Initializes the soapui settings from the specified file. Must be called on
1048 	 * startup before any model items are created.
1049 	 * 
1050 	 * @param fileName
1051 	 */
1052 
1053 	public static void initSettings( String fileName )
1054 	{
1055 		try
1056 		{
1057 			File settingsFile = new File( fileName );
1058 			settingsDocument = SoapuiSettingsDocumentConfig.Factory.parse( settingsFile );
1059 			log.info( "initialized soapui-settings from [" + fileName + "]" );
1060 		}
1061 		catch( Exception e )
1062 		{
1063 			log.warn( "Failed to load settings [" + e.getMessage() + "], creating new " );
1064 			settingsDocument = SoapuiSettingsDocumentConfig.Factory.newInstance();
1065 		}
1066 	}
1067 
1068 	public static void selectModelItem( ModelItem modelItem )
1069 	{
1070 		if( instance != null && instance.navigator != null )
1071 			instance.navigator.selectModelItem( modelItem );
1072 	}
1073 
1074 	// instance is null in Eclipse. /Lars
1075 	// eclipse-version(s) should provide SoapUIDesktop implementation
1076 	public static SoapUIDesktop getDesktop()
1077 	{
1078 		return instance != null ? instance.desktop : null;
1079 	}
1080 
1081 	public static void setDesktop( SoapUIDesktop desktop )
1082 	{
1083 		if( instance != null )
1084 			instance.desktop = desktop;
1085 	}
1086 
1087 	public static Navigator getNavigator()
1088 	{
1089 		return instance != null ? instance.navigator : null;
1090 	}
1091 
1092 	public static void setNavigator( Navigator navigator )
1093 	{
1094 		if( instance != null )
1095 			instance.navigator = navigator;
1096 	}
1097 
1098 //	public static SoapUIProfile getProfile()
1099 //	{
1100 //		return  profile;
1101 //	}
1102 
1103 	static class SoapUISplash extends JWindow
1104 	{
1105 		public SoapUISplash( URL splashImage )
1106 		{
1107 			super( frame );
1108 			JLabel l = new JLabel( new ImageIcon( splashImage ) );
1109 			getContentPane().add( l, BorderLayout.CENTER );
1110 			pack();
1111 			Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
1112 			Dimension labelSize = l.getPreferredSize();
1113 			setLocation( screenSize.width / 2 - ( labelSize.width / 2 ), screenSize.height / 2
1114 						- ( labelSize.height / 2 ) );
1115 			addMouseListener( new MouseAdapter()
1116 			{
1117 				public void mousePressed( MouseEvent e )
1118 				{
1119 					if( frame.isVisible())
1120 					{
1121 						setVisible( false );
1122 						dispose();
1123 					}
1124 				}
1125 			} );
1126 			final int pause = 1000;
1127 			final Runnable closerRunner = new Runnable()
1128 			{
1129 				public void run()
1130 				{
1131 					setVisible( false );
1132 					dispose();
1133 				}
1134 			};
1135 			
1136 			Runnable waitRunner = new Runnable()
1137 			{
1138 				public void run()
1139 				{
1140 					try
1141 					{
1142 						do
1143 						{
1144 							Thread.sleep( pause );
1145 						}
1146 						while( !frame.isVisible() );
1147 						
1148 						SwingUtilities.invokeAndWait( closerRunner );
1149 					}
1150 					catch( Exception e )
1151 					{
1152 						e.printStackTrace();
1153 						// can catch InvocationTargetException
1154 						// can catch InterruptedException
1155 					}
1156 				}
1157 			};
1158 			setVisible( true );
1159 			Thread splashThread = new Thread( waitRunner, "SplashThread" );
1160 			splashThread.start();
1161 		}
1162 	}
1163 
1164 	public static MockEngine getMockEngine()
1165 	{
1166 		if( mockEngine == null )
1167 			mockEngine = new MockEngine();
1168 			
1169 		return mockEngine;
1170 	}
1171 
1172 	public static String getSchemaDirectory()
1173 	{
1174 		return SoapUI.getSettings().getString( WsdlSettings.SCHEMA_DIRECTORY, null );
1175 	}
1176 
1177 	public static Collection<? extends QName> getExcludedTypes()
1178 	{
1179 		List<QName> result = new ArrayList<QName>();
1180 		
1181 		String excluded = SoapUI.getSettings().getString( WsdlSettings.EXCLUDED_TYPES, null );
1182       if( excluded != null && excluded.trim().length() > 0 )
1183       {
1184     	  	try
1185 			{
1186 			   StringList names = StringList.fromXml( excluded );
1187 			   for( String name : names )
1188 			   {
1189 			   	int ix = name.indexOf( '@' );
1190 			   	result.add( new QName( name.substring( ix+1 ), name.substring( 0, ix )));
1191 			   }
1192 		 	}
1193 			catch( XmlException e )
1194 			{
1195 				e.printStackTrace();
1196 			}
1197       }
1198       
1199 		return result;
1200 	}
1201 }