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