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.impl.wsdl.panels.mock;
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.event.ActionEvent;
20  import java.awt.event.ActionListener;
21  import java.beans.PropertyChangeEvent;
22  import java.beans.PropertyChangeListener;
23  import java.text.SimpleDateFormat;
24  import java.util.ArrayList;
25  import java.util.Date;
26  import java.util.LinkedList;
27  import java.util.List;
28  
29  import javax.swing.AbstractAction;
30  import javax.swing.AbstractListModel;
31  import javax.swing.Action;
32  import javax.swing.BorderFactory;
33  import javax.swing.JButton;
34  import javax.swing.JCheckBox;
35  import javax.swing.JComponent;
36  import javax.swing.JLabel;
37  import javax.swing.JList;
38  import javax.swing.JPanel;
39  import javax.swing.JProgressBar;
40  import javax.swing.JScrollPane;
41  import javax.swing.JTextField;
42  import javax.swing.ListCellRenderer;
43  import javax.swing.ListModel;
44  import javax.swing.text.Document;
45  
46  import com.eviware.soapui.SoapUI;
47  import com.eviware.soapui.impl.wsdl.actions.support.ShowOnlineHelpAction;
48  import com.eviware.soapui.impl.wsdl.mock.WsdlMockOperation;
49  import com.eviware.soapui.impl.wsdl.mock.WsdlMockRunner;
50  import com.eviware.soapui.impl.wsdl.mock.WsdlMockService;
51  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
52  import com.eviware.soapui.model.ModelItem;
53  import com.eviware.soapui.model.mock.MockOperation;
54  import com.eviware.soapui.model.mock.MockResponse;
55  import com.eviware.soapui.model.mock.MockResult;
56  import com.eviware.soapui.model.mock.MockServiceListener;
57  import com.eviware.soapui.model.support.MockRunListenerAdapter;
58  import com.eviware.soapui.support.DocumentListenerAdapter;
59  import com.eviware.soapui.support.UISupport;
60  import com.eviware.soapui.support.action.swing.ActionList;
61  import com.eviware.soapui.support.components.JComponentInspector;
62  import com.eviware.soapui.support.components.JInspectorPanel;
63  import com.eviware.soapui.support.components.JXToolBar;
64  import com.eviware.soapui.support.swing.ListMouseListener;
65  import com.eviware.soapui.support.swing.ModelItemListMouseListener;
66  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
67  
68  /***
69   * DesktopPanel for WsdlMockServices
70   * 
71   * @author ole.matzura
72   */
73  
74  public class WsdlMockServiceDesktopPanel extends ModelItemDesktopPanel<WsdlMockService>
75  {
76  	private JButton runButton;
77  	private WsdlMockRunner mockRunner;
78  	private JButton stopButton;
79  	private JProgressBar progressBar;
80  	private LogListModel logListModel;
81  	private JList testLogList;
82  	private JCheckBox enableLogCheckBox;
83  	private JScrollPane logScrollPane;
84  	private JList operationList;
85  	private InternalMockRunListener mockRunListener;
86  //	private JButton optionsButton;
87  	private JTextField pathTextField;
88  	private JTextField portTextField;
89  
90  	public WsdlMockServiceDesktopPanel( WsdlMockService mockService )
91  	{
92  		super( mockService );
93  		buildUI();
94  		
95  		setPreferredSize( new Dimension( 400, 500 ) );
96  		
97  		mockRunListener = new InternalMockRunListener();
98  		mockService.addMockRunListener( mockRunListener  );
99  	}
100 	
101 	public boolean onClose( boolean canCancel )
102 	{
103 		if( mockRunner != null && canCancel )
104 		{
105 			if( !UISupport.confirm( "Close and stop MockService", "Close MockService" ))
106 			{
107 				return false;
108 			}
109 		}
110 		
111 		if( mockRunner != null )
112 		{
113 			mockRunner.stop();
114 			mockRunner.release();
115 		}
116 		
117 		getModelItem().removeMockRunListener( mockRunListener );
118 		((OperationListModel)operationList.getModel()).release();
119 		
120 		logListModel.clear();
121 		
122 		return release();
123 	}
124 
125 	public boolean dependsOn(ModelItem modelItem)
126 	{
127 		return modelItem == getModelItem() || modelItem == getModelItem().getProject();
128 	}
129 	
130 	private void buildUI()
131 	{
132       add( buildToolbar(), BorderLayout.NORTH );
133       
134       JInspectorPanel inspectorPanel = new JInspectorPanel( buildOperationList() );
135       inspectorPanel.setDefaultDividerLocation( 0.5F );
136       inspectorPanel.addInspector( new JComponentInspector( 
137       			buildLog(), "Message Log", "A log of processed requests and their responses", true ));
138       
139       inspectorPanel.setCurrentInspector( "Message Log" );
140       
141       add( inspectorPanel, BorderLayout.CENTER );
142       add( new JLabel( "--"), BorderLayout.PAGE_END );
143 	}
144 
145    public boolean logIsEnabled()
146    {
147    	return enableLogCheckBox.isSelected();
148    }
149    
150 	private JComponent buildOperationList()
151 	{
152 		operationList = new JList( new OperationListModel() );
153 		operationList.addMouseListener( new ModelItemListMouseListener() );
154 		operationList.setCellRenderer( new OperationListCellRenderer() );
155 		
156 		JScrollPane scrollPane = new JScrollPane( operationList );
157 		UISupport.addTitledBorder( scrollPane, "Operations" );
158 		return scrollPane;
159 	}
160 	
161 	private JComponent buildLog()
162 	{
163 		JPanel panel = new JPanel( new BorderLayout() );
164 		JXToolBar builder = UISupport.createToolbar();
165 		
166 		enableLogCheckBox = new JCheckBox( " ", true);
167 		enableLogCheckBox.addActionListener( new ActionListener() {
168 
169 			public void actionPerformed( ActionEvent arg0 )
170 			{
171 				testLogList.setEnabled( enableLogCheckBox.isSelected() );
172 				
173 				// border needs to be repainted..
174 				logScrollPane.repaint();
175 			}}  );
176 		
177 		builder.addFixed( enableLogCheckBox );
178 		builder.addRelatedGap();
179 		builder.addFixed( new JLabel( "Enable"));
180 		builder.addRelatedGap();
181 		addLogActions( builder );
182 		
183 		builder.addGlue();
184 		builder.setBorder( BorderFactory.createEmptyBorder( 2, 3, 3, 3 ) );
185 
186 		panel.add( builder, BorderLayout.NORTH );
187 		
188 		logListModel = new LogListModel();
189 		testLogList = new JList(logListModel);
190 		testLogList.setCellRenderer(new LogCellRenderer());
191 		testLogList.setPrototypeCellValue( "Testing 123" );
192 		testLogList.setFixedCellWidth( -1 );
193 		testLogList.addMouseListener(new LogListMouseListener());
194 		testLogList.setBorder( BorderFactory.createLineBorder(  Color.GRAY ) );
195 
196 		logScrollPane = new JScrollPane(testLogList);
197 //		logScrollPane.setBorder( null );
198 //		UISupport.addTitledBorder( logScrollPane, LOG_TITLE );
199 		
200 		panel.add( logScrollPane, BorderLayout.CENTER );
201 		
202 		return panel;
203 	}
204 
205 	protected void addLogActions( JXToolBar builder )
206 	{
207 		builder.addFixed( new JButton( new ClearLogAction() ) );
208 		builder.addRelatedGap();
209 		builder.addFixed( new JButton( new SetLogOptionsAction() ) );
210 	}
211 
212 	private Component buildToolbar()
213 	{
214 		JXToolBar toolbar = UISupport.createToolbar();
215 		
216 		runButton = createActionButton(new RunMockServiceAction(), true);
217 		stopButton = createActionButton(new StopMockServiceAction(), false);
218 //		optionsButton = createActionButton( new MockServiceOptionsAction( getModelItem() ), true );
219 		
220 		toolbar.addFixed( runButton );
221 		toolbar.addFixed( stopButton );
222 		toolbar.addUnrelatedGap();
223 		
224 		pathTextField = new JTextField(10);
225 		pathTextField.setToolTipText( "The URL path to listen on for this service" );
226 		pathTextField.setText( getModelItem().getPath() );
227 		pathTextField.setCaretPosition( 0 );
228 		pathTextField.getDocument().addDocumentListener( new DocumentListenerAdapter() {
229 
230 			@Override
231 			public void update( Document document )
232 			{
233 				getModelItem().setPath( pathTextField.getText() );
234 			}}  );
235 		
236 		toolbar.addLabeledFixed( "Path:", pathTextField );
237 		toolbar.addUnrelatedGap();
238 		
239 		portTextField = new JTextField(5);
240 		portTextField.setToolTipText( "The local port to listen on for this service" );
241 		portTextField.setText( String.valueOf( getModelItem().getPort() ));
242 		portTextField.getDocument().addDocumentListener( new DocumentListenerAdapter() {
243 
244 			@Override
245 			public void update( Document document )
246 			{
247 				try
248 				{
249 					getModelItem().setPort( Integer.parseInt( portTextField.getText() ) );
250 				}
251 				catch( Exception e )
252 				{
253 				}				
254 			}}  );
255 		
256 		toolbar.addLabeledFixed( "Port:", portTextField );
257 		
258 		toolbar.addGlue();
259 		progressBar = new JProgressBar();
260       JPanel progressBarPanel = UISupport.createProgressBarPanel( progressBar, 2, false );
261       progressBarPanel.setPreferredSize( new Dimension( 60, 20 ) );
262       
263       toolbar.addFixed( progressBarPanel);
264       toolbar.addRelatedGap();
265 		
266 	//	toolbar.addFixed( optionsButton );
267 		toolbar.addFixed( createActionButton( new ShowOnlineHelpAction(HelpUrls.MOCKSERVICE_HELP_URL), true ) );
268 			
269 		return toolbar;
270 	}
271 	
272 	public void startMockService()
273 	{
274 		if( mockRunner != null || SoapUI.getMockEngine().hasRunningMock( getModelItem() ) )
275 		{
276 			UISupport.showErrorMessage( "MockService is already running" );
277 		}
278 		else
279 		{
280 			try
281 			{
282 				mockRunner = getModelItem().start();
283 				mockRunner.setMaxResults( logListModel.getMaxSize() );
284 			}
285 			catch( Exception e )
286 			{
287 				UISupport.showErrorMessage( e );
288 				return;
289 			}
290 		}
291 		
292 		progressBar.setIndeterminate( true );
293 		
294 		runButton.setEnabled( false );
295 		stopButton.setEnabled( true );
296 		pathTextField.setEnabled( false );
297 		portTextField.setEnabled( false );
298 	}
299 
300 	private final class InternalMockRunListener extends MockRunListenerAdapter
301 	{
302 		public void onMockResult( MockResult result )
303 		{
304 			if( logIsEnabled() )
305 			{
306 				logListModel.addElement( result );
307 			}
308 		}
309 	}
310 
311 	public class OperationListModel extends AbstractListModel implements ListModel, MockServiceListener, PropertyChangeListener
312 	{
313 		private List<WsdlMockOperation> operations = new ArrayList<WsdlMockOperation>();
314 		
315 		public OperationListModel()
316 		{
317 			for( int c = 0; c < getModelItem().getMockOperationCount(); c++ )
318 			{
319 				WsdlMockOperation mockOperation = ( WsdlMockOperation ) getModelItem().getMockOperationAt( c );
320 				mockOperation.addPropertyChangeListener( this );
321 				
322 				operations.add( mockOperation);
323 			}
324 			
325 			getModelItem().addMockServiceListener( this );
326 		}
327 		
328 		public Object getElementAt( int arg0 )
329 		{
330 			return operations.get( arg0 );
331 		}
332 
333 		public int getSize()
334 		{
335 			return operations.size();
336 		}
337 
338 		public void mockOperationAdded( MockOperation operation )
339 		{
340 			operations.add( ( WsdlMockOperation ) operation );
341 			operation.addPropertyChangeListener( this );
342 			fireIntervalAdded( this, operations.size()-1, operations.size()-1 );
343 		}
344 
345 		public void mockOperationRemoved( MockOperation operation )
346 		{
347 			int ix = operations.indexOf( operation );
348 			operations.remove( ix );
349 			operation.removePropertyChangeListener( this );
350 			fireIntervalRemoved( this, ix, ix );
351 		}
352 
353 		public void mockResponseAdded( MockResponse request )
354 		{
355 		}
356 
357 		public void mockResponseRemoved( MockResponse request )
358 		{
359 		}
360 
361 		public void propertyChange( PropertyChangeEvent arg0 )
362 		{
363 			if( arg0.getPropertyName().equals( WsdlMockOperation.NAME_PROPERTY ))
364 			{
365 				int ix = operations.indexOf( arg0.getSource() );
366 				fireContentsChanged( this, ix, ix );
367 			}
368 		}
369 		
370 		public void release()
371 		{
372 			for( WsdlMockOperation operation : operations )
373 			{
374 				operation.removePropertyChangeListener( this );
375 			}
376 			
377 			getModelItem().removeMockServiceListener( this );
378 		}
379 	}
380 	
381 	private final static class OperationListCellRenderer extends JLabel implements ListCellRenderer
382 	{
383 		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
384 				boolean cellHasFocus)
385 		{
386 			MockOperation testStep = (MockOperation) value;
387 			setText(testStep.getName());
388 			setIcon(testStep.getIcon());
389 
390 			if (isSelected)
391 			{
392 				setBackground(list.getSelectionBackground());
393 				setForeground(list.getSelectionForeground());
394 			}
395 			else
396 			{
397 				setBackground(list.getBackground());
398 				setForeground(list.getForeground());
399 			}
400 
401 			setEnabled(list.isEnabled());
402 			setFont(list.getFont());
403 			setOpaque(true);
404 			setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
405 
406 			return this;
407 		}
408 	}
409 	
410 	public class RunMockServiceAction extends AbstractAction
411 	{
412 		public RunMockServiceAction()
413 		{
414 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/submit_request.gif"));
415 			putValue(Action.SHORT_DESCRIPTION, "Starts this MockService on the specified port and endpoint");
416 			putValue(Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt ENTER" ));
417 		}
418 		
419 		public void actionPerformed( ActionEvent arg0 )
420 		{
421 			startMockService();
422 		}
423 	}
424 	
425 	public class StopMockServiceAction extends AbstractAction
426 	{
427 		public StopMockServiceAction()
428 		{
429 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/cancel_request.gif"));
430 			putValue(Action.SHORT_DESCRIPTION, "Stops this MockService on the specified port and endpoint");
431 		}
432 		
433 		public void actionPerformed( ActionEvent arg0 )
434 		{
435 			if( mockRunner == null )
436 			{
437 				UISupport.showErrorMessage( "MockService is not running" );
438 			}
439 			else
440 			{
441 				mockRunner.stop();
442 				mockRunner.release();
443 				mockRunner = null;
444 			}
445 			
446 			progressBar.setIndeterminate( false );
447 			
448 			runButton.setEnabled( true );
449 			stopButton.setEnabled( false );
450 			pathTextField.setEnabled( true );
451 			portTextField.setEnabled( true );
452 		}
453 	}
454 	
455 	private static final class LogCellRenderer extends JLabel implements ListCellRenderer
456 	{
457 		private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
458 		
459 		public LogCellRenderer()
460 		{
461 			setOpaque(true);
462 			setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
463 		}
464 		
465 		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
466 				boolean cellHasFocus)
467 		{
468 			if (value instanceof String)
469 			{
470 				setText(value.toString());
471 			}
472 			else if (value instanceof MockResult)
473 			{
474 				MockResult result = ( MockResult ) value;
475 				String msg = dateFormat.format( new Date( result.getTimestamp()) );
476 				MockResponse mockResponse = result.getMockResponse();
477 				
478 				if( mockResponse == null )
479 				{
480 					msg += ": [dispatch error; missing response]";
481 				}
482 				else
483 				{
484 					msg += ": [" + mockResponse.getMockOperation().getName();
485 					msg += "] " + result.getTimeTaken() + "ms";
486 				}
487 				
488 				setText( msg);
489 			}
490 
491 			if (isSelected)
492 			{
493 				setBackground(list.getSelectionBackground());
494 				setForeground(list.getSelectionForeground());
495 			}
496 			else
497 			{
498 				setBackground(list.getBackground());
499 				setForeground(list.getForeground());
500 			}
501 
502 			setEnabled(list.isEnabled());
503 
504 			return this;
505 		}
506 	}
507 	
508 	private class LogListModel extends AbstractListModel
509 	{
510 		private List<MockResult> elements = new LinkedList<MockResult>();
511 		private long maxSize = 100;
512 		
513 		public synchronized void addElement( MockResult result )
514 		{
515 			elements.add( result );
516 			fireIntervalAdded( this, elements.size()-1,  elements.size()-1 );
517 			
518 			while( elements.size() > maxSize )
519 			{
520 				removeElementAt( 0 );
521 			}
522 		}
523 		
524 		public synchronized Object getElementAt( int index )
525 		{
526 			return elements.get( index );
527 		}
528 
529 		public synchronized void removeElementAt( int index )
530 		{
531 			elements.remove( index );
532 			fireIntervalRemoved( this, index, index );
533 		}
534 		
535 		public synchronized void clear()
536 		{
537 			int sz = elements.size();
538 			if( sz > 0 )
539 			{
540 				elements.clear();
541 				fireIntervalRemoved( this, 0, sz-1 );
542 			}
543 		}
544 		
545 		public synchronized int getSize()
546 		{
547 			return elements.size();
548 		}
549 
550 		public long getMaxSize()
551 		{
552 			return maxSize;
553 		}
554 
555 		public synchronized void setMaxSize( long l )
556 		{
557 			this.maxSize = l;
558 			
559 			while( elements.size() > maxSize )
560 			{
561 				removeElementAt( 0 );
562 			}
563 		}
564 	}
565 	
566 	private class SetLogOptionsAction extends AbstractAction
567 	{
568 		public SetLogOptionsAction()
569 		{
570 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/options.gif" ));
571 			putValue( Action.SHORT_DESCRIPTION, "Sets MockService Log Options");
572 		}
573 		
574 		public void actionPerformed( ActionEvent e )
575 		{
576 			String s = UISupport.prompt( "Enter maximum number of rows for MockService Log", "Log Options", String.valueOf( logListModel.getMaxSize() ) );
577 			if( s != null )
578 			{
579 				try
580 				{
581 					logListModel.setMaxSize( Long.parseLong( s ));
582 					if( mockRunner != null )
583 						mockRunner.setMaxResults( logListModel.getMaxSize() );
584 				}
585 				catch( NumberFormatException e1 )
586 				{
587 				}
588 			}
589 		}}
590 	
591 	private class ClearLogAction extends AbstractAction
592 	{
593 		public ClearLogAction()
594 		{
595 			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/clear_loadtest.gif" ));
596 			putValue( Action.SHORT_DESCRIPTION, "Clears the MockService Log");
597 		}
598 		
599 		public void actionPerformed( ActionEvent e )
600 		{
601 			logListModel.clear();
602 			if( mockRunner != null )
603 				mockRunner.clearResults();
604 		}}
605 	
606 	/***
607 	 * Mouse Listener for triggering default action and showing popup for log list items
608 	 * 
609 	 * @author Ole.Matzura
610 	 */
611 
612 	private final class LogListMouseListener extends ListMouseListener
613 	{
614 		@Override
615 		protected ActionList getActionsForRow( JList list, int row )
616 		{
617 			MockResult result = ( MockResult ) logListModel.getElementAt( row );
618 			return result == null ? null : result.getActions();
619 		}
620 	}
621 }