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.List;
27  
28  import javax.swing.AbstractAction;
29  import javax.swing.AbstractListModel;
30  import javax.swing.Action;
31  import javax.swing.BorderFactory;
32  import javax.swing.DefaultListModel;
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.WsdlMockService;
50  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
51  import com.eviware.soapui.model.ModelItem;
52  import com.eviware.soapui.model.mock.MockOperation;
53  import com.eviware.soapui.model.mock.MockResponse;
54  import com.eviware.soapui.model.mock.MockResult;
55  import com.eviware.soapui.model.mock.MockRunner;
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.ActionList;
61  import com.eviware.soapui.support.components.JXToolBar;
62  import com.eviware.soapui.ui.support.ModelItemDesktopPanel;
63  import com.jgoodies.forms.builder.ButtonBarBuilder;
64  
65  public class WsdlMockServiceDesktopPanel extends ModelItemDesktopPanel<WsdlMockService>
66  {
67  	private static final String LOG_TITLE = "Request Log";
68  	private JButton runButton;
69  	private MockRunner mockRunner;
70  	private JButton stopButton;
71  	private JProgressBar progressBar;
72  	private DefaultListModel logListModel;
73  	private JList testLogList;
74  	private JCheckBox enableLogCheckBox;
75  	private JScrollPane logScrollPane;
76  	private JList operationList;
77  	private InternalMockRunListener mockRunListener;
78  //	private JButton optionsButton;
79  	private JTextField pathTextField;
80  	private JTextField portTextField;
81  
82  	public WsdlMockServiceDesktopPanel( WsdlMockService mockService )
83  	{
84  		super( mockService );
85  		buildUI();
86  		
87  		setPreferredSize( new Dimension( 350, 500 ) );
88  		
89  		mockRunListener = new InternalMockRunListener();
90  		mockService.addMockRunListener( mockRunListener  );
91  	}
92  	
93  	public boolean onClose( boolean canCancel )
94  	{
95  		if( mockRunner != null && canCancel )
96  		{
97  			if( !UISupport.confirm( "Close and stop MockService", "Close MockService" ))
98  			{
99  				return false;
100 			}
101 		}
102 		
103 		if( mockRunner != null )
104 		{
105 			mockRunner.stop();
106 		}
107 		
108 		getModelItem().removeMockRunListener( mockRunListener );
109 		((OperationListModel)operationList.getModel()).release();
110 		
111 		return release();
112 	}
113 
114 	public boolean dependsOn(ModelItem modelItem)
115 	{
116 		return modelItem == getModelItem() || modelItem == getModelItem().getProject();
117 	}
118 	
119 	private void buildUI()
120 	{
121       add( buildToolbar(), BorderLayout.NORTH );
122       
123       JSplitPane splitPane = UISupport.createVerticalSplit();
124 		splitPane.setTopComponent( buildOperationList() );
125       splitPane.setBottomComponent( buildLog() );
126       splitPane.setDividerLocation( 0.7 );
127       splitPane.setDividerLocation( 250 );
128 
129       add( splitPane, BorderLayout.CENTER );
130       add( new JLabel( "--"), BorderLayout.PAGE_END );
131 	}
132 
133    public boolean logIsEnabled()
134    {
135    	return enableLogCheckBox.isSelected();
136    }
137    
138 	private Component buildOperationList()
139 	{
140 		JPanel panel = new JPanel( new BorderLayout() );
141 		ButtonBarBuilder builder = new ButtonBarBuilder();
142 		
143 		enableLogCheckBox = new JCheckBox("Enable Log", true);
144 		enableLogCheckBox.addActionListener( new ActionListener() {
145 
146 			public void actionPerformed( ActionEvent arg0 )
147 			{
148 				testLogList.setEnabled( enableLogCheckBox.isSelected() );
149 				
150 				// border needs to be repainted..
151 				logScrollPane.repaint();
152 			}}  );
153 		
154 		builder.addFixed( enableLogCheckBox );
155 		builder.addGlue();
156 		
157 		progressBar = new JProgressBar();
158       JPanel progressBarPanel = UISupport.createProgressBarPanel( progressBar, 2, false );
159       progressBarPanel.setPreferredSize( new Dimension( 60, 20 ) );
160       
161 		builder.addFixed( progressBarPanel);
162 		builder.setBorder( BorderFactory.createEmptyBorder( 2, 3, 3, 3 ) );
163 
164 		panel.add( builder.getPanel(), BorderLayout.NORTH );
165 		
166 		operationList = new JList( new OperationListModel() );
167 		operationList.addMouseListener( new ModelItemListMouseListener() );
168 		operationList.setCellRenderer( new OperationListCellRenderer() );
169 		
170 		JScrollPane scrollPane = new JScrollPane( operationList );
171 		UISupport.addTitledBorder( scrollPane, "Operations" );
172 		panel.add( scrollPane, BorderLayout.CENTER );
173 		
174 		return panel;
175 	}
176 	
177 	private Component buildLog()
178 	{
179 		logListModel = new DefaultListModel();
180 		testLogList = new JList(logListModel);
181 		testLogList.setCellRenderer(new LogCellRenderer());
182 		testLogList.setPrototypeCellValue( "Testing 123" );
183 		testLogList.setFixedCellWidth( -1 );
184 		testLogList.addMouseListener(new LogListMouseListener());
185 		testLogList.setBorder( BorderFactory.createLineBorder(  Color.GRAY ) );
186 
187 		logScrollPane = new JScrollPane(testLogList);
188 		logScrollPane.setBorder( null );
189 		UISupport.addTitledBorder( logScrollPane, LOG_TITLE );
190 		return logScrollPane;
191 	}
192 
193 	private Component buildToolbar()
194 	{
195 		JXToolBar toolbar = UISupport.createToolbar();
196 		
197 		runButton = createActionButton(new RunMockServiceAction(), true);
198 		stopButton = createActionButton(new StopMockServiceAction(), false);
199 //		optionsButton = createActionButton( new MockServiceOptionsAction( getModelItem() ), true );
200 		
201 		toolbar.addFixed( runButton );
202 		toolbar.addFixed( stopButton );
203 		toolbar.addUnrelatedGap();
204 		
205 		pathTextField = new JTextField(15);
206 		pathTextField.setToolTipText( "The URL path to listen on for this service" );
207 		pathTextField.setText( getModelItem().getPath() );
208 		pathTextField.setCaretPosition( 0 );
209 		pathTextField.getDocument().addDocumentListener( new DocumentListenerAdapter() {
210 
211 			@Override
212 			public void update( Document document )
213 			{
214 				getModelItem().setPath( pathTextField.getText() );
215 			}}  );
216 		
217 		toolbar.addLabeledFixed( "Path:", pathTextField );
218 		toolbar.addUnrelatedGap();
219 		
220 		portTextField = new JTextField(5);
221 		portTextField.setToolTipText( "The local port to listen on for this service" );
222 		portTextField.setText( String.valueOf( getModelItem().getPort() ));
223 		portTextField.getDocument().addDocumentListener( new DocumentListenerAdapter() {
224 
225 			@Override
226 			public void update( Document document )
227 			{
228 				try
229 				{
230 					getModelItem().setPort( Integer.parseInt( portTextField.getText() ) );
231 				}
232 				catch( Exception e )
233 				{
234 				}				
235 			}}  );
236 		
237 		toolbar.addLabeledFixed( "Port:", portTextField );
238 		
239 		toolbar.addGlue();
240 	//	toolbar.addFixed( optionsButton );
241 		toolbar.addFixed( createActionButton( new ShowOnlineHelpAction(HelpUrls.MOCKSERVICE_HELP_URL), true ) );
242 			
243 		return toolbar;
244 	}
245 	
246 	private final class InternalMockRunListener extends MockRunListenerAdapter
247 	{
248 		public void onMockResult( MockResult result )
249 		{
250 			if( logIsEnabled() )
251 			{
252 				logListModel.addElement( result );
253 			}
254 		}
255 	}
256 
257 	public class OperationListModel extends AbstractListModel implements ListModel, MockServiceListener, PropertyChangeListener
258 	{
259 		private List<WsdlMockOperation> operations = new ArrayList<WsdlMockOperation>();
260 		
261 		public OperationListModel()
262 		{
263 			for( int c = 0; c < getModelItem().getMockOperationCount(); c++ )
264 			{
265 				WsdlMockOperation mockOperation = ( WsdlMockOperation ) getModelItem().getMockOperationAt( c );
266 				mockOperation.addPropertyChangeListener( this );
267 				
268 				operations.add( mockOperation);
269 			}
270 			
271 			getModelItem().addMockServiceListener( this );
272 		}
273 		
274 		public Object getElementAt( int arg0 )
275 		{
276 			return operations.get( arg0 );
277 		}
278 
279 		public int getSize()
280 		{
281 			return operations.size();
282 		}
283 
284 		public void mockOperationAdded( MockOperation operation )
285 		{
286 			operations.add( ( WsdlMockOperation ) operation );
287 			operation.addPropertyChangeListener( this );
288 			fireIntervalAdded( this, operations.size()-1, operations.size()-1 );
289 		}
290 
291 		public void mockOperationRemoved( MockOperation operation )
292 		{
293 			int ix = operations.indexOf( operation );
294 			operations.remove( ix );
295 			operation.removePropertyChangeListener( this );
296 			fireIntervalRemoved( this, ix, ix );
297 		}
298 
299 		public void mockResponseAdded( MockResponse request )
300 		{
301 		}
302 
303 		public void mockResponseRemoved( MockResponse request )
304 		{
305 		}
306 
307 		public void propertyChange( PropertyChangeEvent arg0 )
308 		{
309 			if( arg0.getPropertyName().equals( WsdlMockOperation.NAME_PROPERTY ))
310 			{
311 				int ix = operations.indexOf( arg0.getSource() );
312 				fireContentsChanged( this, ix, ix );
313 			}
314 		}
315 		
316 		public void release()
317 		{
318 			for( WsdlMockOperation operation : operations )
319 			{
320 				operation.removePropertyChangeListener( this );
321 			}
322 			
323 			getModelItem().removeMockServiceListener( this );
324 		}
325 	}
326 	
327 	private final static class OperationListCellRenderer extends JLabel implements ListCellRenderer
328 	{
329 		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
330 				boolean cellHasFocus)
331 		{
332 			MockOperation testStep = (MockOperation) value;
333 			setText(testStep.getName());
334 			setIcon(testStep.getIcon());
335 
336 			if (isSelected)
337 			{
338 				setBackground(list.getSelectionBackground());
339 				setForeground(list.getSelectionForeground());
340 			}
341 			else
342 			{
343 				setBackground(list.getBackground());
344 				setForeground(list.getForeground());
345 			}
346 
347 			setEnabled(list.isEnabled());
348 			setFont(list.getFont());
349 			setOpaque(true);
350 			setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
351 
352 			return this;
353 		}
354 	}
355 	
356 	public class RunMockServiceAction extends AbstractAction
357 	{
358 		public RunMockServiceAction()
359 		{
360 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/submit_request.gif"));
361 			putValue(Action.SHORT_DESCRIPTION, "Starts this MockService on the specified port and endpoint");
362 			putValue(Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt ENTER" ));
363 		}
364 		
365 		public void actionPerformed( ActionEvent arg0 )
366 		{
367 			if( mockRunner != null || SoapUI.getMockEngine().hasRunningMock( getModelItem() ) )
368 			{
369 				UISupport.showErrorMessage( "MockService is already running" );
370 			}
371 			else
372 			{
373 				try
374 				{
375 					mockRunner = getModelItem().start();
376 				}
377 				catch( Exception e )
378 				{
379 					UISupport.showErrorMessage( e );
380 					e.printStackTrace();
381 					return;
382 				}
383 			}
384 			
385 			progressBar.setIndeterminate( true );
386 			
387 			runButton.setEnabled( false );
388 			stopButton.setEnabled( true );
389 			pathTextField.setEnabled( false );
390 			portTextField.setEnabled( false );
391 		}
392 	}
393 	
394 	public class StopMockServiceAction extends AbstractAction
395 	{
396 		public StopMockServiceAction()
397 		{
398 			putValue(Action.SMALL_ICON, UISupport.createImageIcon("/cancel_request.gif"));
399 			putValue(Action.SHORT_DESCRIPTION, "Stops this MockService on the specified port and endpoint");
400 		}
401 		
402 		public void actionPerformed( ActionEvent arg0 )
403 		{
404 			if( mockRunner == null )
405 			{
406 				UISupport.showErrorMessage( "MockService is not running" );
407 			}
408 			else
409 			{
410 				mockRunner.stop();
411 				mockRunner = null;
412 			}
413 			
414 			progressBar.setIndeterminate( false );
415 			
416 			runButton.setEnabled( true );
417 			stopButton.setEnabled( false );
418 			pathTextField.setEnabled( true );
419 			portTextField.setEnabled( true );
420 		}
421 	}
422 	
423 	private static final class LogCellRenderer extends JLabel implements ListCellRenderer
424 	{
425 		private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
426 		
427 		public LogCellRenderer()
428 		{
429 			setOpaque(true);
430 			setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
431 		}
432 		
433 		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
434 				boolean cellHasFocus)
435 		{
436 			if (value instanceof String)
437 			{
438 				setText(value.toString());
439 			}
440 			else if (value instanceof MockResult)
441 			{
442 				MockResult result = ( MockResult ) value;
443 				String msg = dateFormat.format( new Date( result.getTimestamp()) );
444 				MockResponse mockResponse = result.getMockResponse();
445 				
446 				if( mockResponse == null )
447 				{
448 					msg += ": [dispatch error; missing response]";
449 				}
450 				else
451 				{
452 					msg += ": [" + mockResponse.getMockOperation().getName();
453 					msg += "] " + result.getTimeTaken() + "ms";
454 				}
455 				
456 				setText( msg);
457 			}
458 
459 			if (isSelected)
460 			{
461 				setBackground(list.getSelectionBackground());
462 				setForeground(list.getSelectionForeground());
463 			}
464 			else
465 			{
466 				setBackground(list.getBackground());
467 				setForeground(list.getForeground());
468 			}
469 
470 			setEnabled(list.isEnabled());
471 
472 			return this;
473 		}
474 	}
475 	
476 	/***
477 	 * Mouse Listener for triggering default action and showing popup for log list items
478 	 * 
479 	 * @author Ole.Matzura
480 	 */
481 
482 	private final class LogListMouseListener extends ListMouseListener
483 	{
484 		@Override
485 		protected ActionList getActionsForRow( JList list, int row )
486 		{
487 			MockResult result = ( MockResult ) logListModel.getElementAt( row );
488 			return result == null ? null : result.getActions();
489 		}
490 	}
491 }