View Javadoc

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