View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2008 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.teststeps.assertions.basic;
14  
15  import com.eviware.soapui.SoapUI;
16  import com.eviware.soapui.config.TestAssertionConfig;
17  import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
18  import com.eviware.soapui.impl.wsdl.WsdlInterface;
19  import com.eviware.soapui.impl.wsdl.support.HelpUrls;
20  import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
21  import com.eviware.soapui.impl.wsdl.teststeps.WsdlMessageAssertion;
22  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
23  import com.eviware.soapui.impl.wsdl.teststeps.assertions.AbstractTestAssertionFactory;
24  import com.eviware.soapui.model.TestModelItem;
25  import com.eviware.soapui.model.iface.MessageExchange;
26  import com.eviware.soapui.model.iface.SubmitContext;
27  import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
28  import com.eviware.soapui.model.propertyexpansion.PropertyExpansionUtils;
29  import com.eviware.soapui.model.support.XPathReference;
30  import com.eviware.soapui.model.support.XPathReferenceContainer;
31  import com.eviware.soapui.model.support.XPathReferenceImpl;
32  import com.eviware.soapui.model.testsuite.*;
33  import com.eviware.soapui.model.testsuite.AssertionError;
34  import com.eviware.soapui.support.StringUtils;
35  import com.eviware.soapui.support.UISupport;
36  import com.eviware.soapui.support.components.JUndoableTextArea;
37  import com.eviware.soapui.support.components.JXToolBar;
38  import com.eviware.soapui.support.xml.XmlObjectConfigurationBuilder;
39  import com.eviware.soapui.support.xml.XmlObjectConfigurationReader;
40  import com.eviware.soapui.support.xml.XmlUtils;
41  import com.jgoodies.forms.builder.ButtonBarBuilder;
42  import org.apache.log4j.Logger;
43  import org.apache.xmlbeans.XmlAnySimpleType;
44  import org.apache.xmlbeans.XmlObject;
45  import org.apache.xmlbeans.XmlOptions;
46  import org.custommonkey.xmlunit.*;
47  import org.w3c.dom.Element;
48  import org.w3c.dom.Node;
49  
50  import javax.swing.*;
51  import java.awt.*;
52  import java.awt.event.ActionEvent;
53  import java.awt.event.WindowAdapter;
54  import java.awt.event.WindowEvent;
55  import java.util.ArrayList;
56  import java.util.List;
57  
58  /***
59   * Assertion that matches a specified XQuery expression and its expected result
60   * against the associated WsdlTestRequests response message
61   * 
62   * @author Ole.Matzura
63   */
64  
65  public class XQueryContainsAssertion extends WsdlMessageAssertion implements RequestAssertion, ResponseAssertion, XPathReferenceContainer
66  {
67  	private final static Logger log = Logger.getLogger( XQueryContainsAssertion.class );
68  	private String expectedContent;
69  	private String path;
70  	private JDialog configurationDialog;
71  	private JTextArea pathArea;
72  	private JTextArea contentArea;
73  	private boolean configureResult;
74  	private boolean allowWildcards;
75  
76  	public static final String ID = "XQuery Match";
77  	public static final String LABEL = "XQuery Match";
78  	private JCheckBox allowWildcardsCheckBox;
79  
80  	public XQueryContainsAssertion( TestAssertionConfig assertionConfig, Assertable assertable )
81  	{
82  		super( assertionConfig, assertable, true, true, true, true );
83  
84  		XmlObjectConfigurationReader reader = new XmlObjectConfigurationReader( getConfiguration() );
85  		path = reader.readString( "path", null );
86  		expectedContent = reader.readString( "content", null );
87  		allowWildcards = reader.readBoolean( "allowWildcards", false );
88  	}
89  
90  	public String getExpectedContent()
91  	{
92  		return expectedContent;
93  	}
94  
95  	public void setExpectedContent( String expectedContent )
96  	{
97  		this.expectedContent = expectedContent;
98  		setConfiguration( createConfiguration() );
99  	}
100 	
101 	/***
102 	 * @deprecated
103 	 */
104 	
105 	public void setContent( String content )
106 	{
107 		setExpectedContent( content );
108 	}
109 
110 	public String getPath()
111 	{
112 		return path;
113 	}
114 
115 	public void setPath( String path )
116 	{
117 		this.path = path;
118 		setConfiguration( createConfiguration() );
119 	}
120 
121 	public boolean isAllowWildcards()
122 	{
123 		return allowWildcards;
124 	}
125 
126 	public void setAllowWildcards( boolean allowWildcards )
127 	{
128 		this.allowWildcards = allowWildcards;
129 	}
130 
131 	protected String internalAssertResponse( MessageExchange messageExchange, SubmitContext context )
132 				throws AssertionException
133 	{
134        if( !messageExchange.hasResponse() )
135          return "Missing Response";
136       else
137          return assertContent( messageExchange.getResponseContentAsXml(), context, "Response" );
138 	}
139 
140 	public String assertContent( String response, SubmitContext context, String type ) throws AssertionException
141 	{
142 		try
143 		{
144 			if( path == null )
145 				return "Missing path for XQuery Assertion";
146 			if( expectedContent == null )
147 				return "Missing content for XQuery Assertion";
148 
149 			XmlObject xml = XmlObject.Factory.parse( response );
150 			String expandedPath = PropertyExpansionUtils.expandProperties( context, path );
151 			XmlObject[] items = xml.execQuery( expandedPath );
152 
153 			XmlObject contentObj = null;
154 			String expandedContent = PropertyExpansionUtils.expandProperties( context, expectedContent );
155 
156 			try
157 			{
158 				contentObj = XmlObject.Factory.parse( expandedContent );
159 			}
160 			catch( Exception e )
161 			{
162 				// this is ok.. it just means that the content to match is not xml
163 				// but
164 				// (hopefully) just a string
165 			}
166 
167 			if( items.length == 0 )
168 				throw new Exception( "Missing content for xquery [" + path + "] in " + type );
169 
170 			XmlOptions options = new XmlOptions();
171 			options.setSavePrettyPrint();
172 			options.setSaveOuter();
173 
174 			for( int c = 0; c < items.length; c++ )
175 			{
176 				try
177 				{
178 					if( contentObj == null )
179 					{
180 						if( items[c] instanceof XmlAnySimpleType )
181 						{
182 							String value = ( ( XmlAnySimpleType ) items[c] ).getStringValue();
183 							String expandedValue = PropertyExpansionUtils.expandProperties( context, value );
184 							XMLAssert.assertEquals( expandedContent, expandedValue );
185 						}
186 						else
187 						{
188 							Node domNode = items[c].getDomNode();
189 							if( domNode.getNodeType() == Node.ELEMENT_NODE )
190 							{
191 								String expandedValue = PropertyExpansionUtils.expandProperties( context, XmlUtils
192 											.getElementText( ( Element ) domNode ) );
193 								XMLAssert.assertEquals( expandedContent, expandedValue );
194 							}
195 							else
196 							{
197 								String expandedValue = PropertyExpansionUtils.expandProperties( context, items[c].xmlText( options ) );
198 								XMLAssert.assertEquals( expandedContent, expandedValue );
199 							}
200 						}
201 					}
202 					else
203 					{
204 						compareValues( contentObj.xmlText( options ), items[c].xmlText( options ) );
205 					}
206 
207 					break;
208 				}
209 				catch( Throwable e )
210 				{
211 					if( c == items.length - 1 )
212 						throw e;
213 				}
214 			}
215 		}
216 		catch( Throwable e )
217 		{
218 			String msg = "XQuery Match Assertion failed for path [" + path + "] : " + e.getClass().getSimpleName() + ":"
219 						+ e.getMessage();
220 
221 			throw new AssertionException( new AssertionError( msg ) );
222 		}
223 
224 		return type + " matches content for [" + path + "]";
225 	}
226 
227 	private void compareValues( String expandedContent, String expandedValue ) throws Exception
228 	{
229 		Diff diff = new Diff( expandedContent, expandedValue );
230 		diff.overrideDifferenceListener( new DifferenceListener()
231 		{
232 
233 			public int differenceFound( Difference diff )
234 			{
235 				if( allowWildcards
236 							&& ( diff.getId() == DifferenceEngine.TEXT_VALUE.getId() || diff.getId() == DifferenceEngine.ATTR_VALUE
237 										.getId() ) )
238 				{
239 					if( diff.getControlNodeDetail().getValue().equals( "*" ) )
240 						return Diff.RETURN_IGNORE_DIFFERENCE_NODES_IDENTICAL;
241 				}
242 
243 				return Diff.RETURN_ACCEPT_DIFFERENCE;
244 			}
245 
246 			public void skippedComparison( Node arg0, Node arg1 )
247 			{
248 
249 			}
250 		} );
251 
252 		if( !diff.identical() )
253 			throw new Exception( diff.toString() );
254 	}
255 
256 	public boolean configure()
257 	{
258 		if( configurationDialog == null )
259 			buildConfigurationDialog();
260 
261 		pathArea.setText( path );
262 		contentArea.setText( expectedContent );
263 		allowWildcardsCheckBox.setSelected( allowWildcards );
264 
265 		UISupport.showDialog( configurationDialog );
266 		return configureResult;
267 	}
268 
269 	protected void buildConfigurationDialog()
270 	{
271 		configurationDialog = new JDialog( UISupport.getMainFrame() );
272 		configurationDialog.setTitle( "XQuery Match configuration" );
273 		configurationDialog.addWindowListener( new WindowAdapter()
274 		{
275 			public void windowOpened( WindowEvent event )
276 			{
277 				SwingUtilities.invokeLater( new Runnable()
278 				{
279 					public void run()
280 					{
281 						pathArea.requestFocusInWindow();
282 					}
283 				} );
284 			}
285 		} );
286 
287 		JPanel contentPanel = new JPanel( new BorderLayout() );
288 		contentPanel.add( UISupport.buildDescription( "Specify XQuery expression and expected result",
289 					"declare namespaces with <code>declare namespace &lt;prefix&gt;='&lt;namespace&gt;';</code>", null ),
290 					BorderLayout.NORTH );
291 
292 		JSplitPane splitPane = UISupport.createVerticalSplit();
293 
294 		JPanel pathPanel = new JPanel( new BorderLayout() );
295 		JXToolBar pathToolbar = UISupport.createToolbar();
296 		addPathEditorActions( pathToolbar );
297 
298 		pathArea = new JUndoableTextArea();
299 		pathArea.setToolTipText( "Specifies the XQuery expression to select from the message for validation" );
300 
301 		pathPanel.add( pathToolbar, BorderLayout.NORTH );
302 		pathPanel.add( new JScrollPane( pathArea ), BorderLayout.CENTER );
303 
304 		splitPane.setTopComponent( UISupport.addTitledBorder( pathPanel, "XQuery Expression" ) );
305 
306 		JPanel matchPanel = new JPanel( new BorderLayout() );
307 		JXToolBar contentToolbar = UISupport.createToolbar();
308 		addMatchEditorActions( contentToolbar );
309 
310 		contentArea = new JUndoableTextArea();
311 		contentArea.setToolTipText( "Specifies the expected result of the XQuery expression" );
312 
313 		matchPanel.add( contentToolbar, BorderLayout.NORTH );
314 		matchPanel.add( new JScrollPane( contentArea ), BorderLayout.CENTER );
315 
316 		splitPane.setBottomComponent( UISupport.addTitledBorder( matchPanel, "Expected Result" ) );
317 		splitPane.setDividerLocation( 150 );
318 		splitPane.setBorder( BorderFactory.createEmptyBorder( 0, 1, 0, 1 ) );
319 
320 		contentPanel.add( splitPane, BorderLayout.CENTER );
321 
322 		ButtonBarBuilder builder = new ButtonBarBuilder();
323 
324 		ShowOnlineHelpAction showOnlineHelpAction = new ShowOnlineHelpAction( HelpUrls.XQUERYASSERTIONEDITOR_HELP_URL );
325 		builder.addFixed( UISupport.createToolbarButton( showOnlineHelpAction ) );
326 		builder.addGlue();
327 
328 		JButton okButton = new JButton( new OkAction() );
329 		builder.addFixed( okButton );
330 		builder.addRelatedGap();
331 		builder.addFixed( new JButton( new CancelAction() ) );
332 
333 		builder.setBorder( BorderFactory.createEmptyBorder( 1, 5, 5, 5 ) );
334 
335 		contentPanel.add( builder.getPanel(), BorderLayout.SOUTH );
336 
337 		configurationDialog.setContentPane( contentPanel );
338 		configurationDialog.setSize( 600, 500 );
339 		configurationDialog.setModal( true );
340 		UISupport.initDialogActions( configurationDialog, showOnlineHelpAction, okButton );
341 	}
342 
343 	protected void addPathEditorActions( JXToolBar toolbar )
344 	{
345 		toolbar.addFixed( new JButton( new DeclareNamespacesFromCurrentAction() ) );
346 	}
347 
348 	protected void addMatchEditorActions( JXToolBar toolbar )
349 	{
350 		toolbar.addFixed( new JButton( new SelectFromCurrentAction() ) );
351 		toolbar.addRelatedGap();
352 		toolbar.addFixed( new JButton( new TestPathAction() ) );
353 		allowWildcardsCheckBox = new JCheckBox( "Allow Wildcards" );
354 
355 		Dimension dim = new Dimension( 100, 20 );
356 
357 		allowWildcardsCheckBox.setSize( dim );
358 		allowWildcardsCheckBox.setPreferredSize( dim );
359 
360 		allowWildcardsCheckBox.setOpaque( false );
361 		toolbar.addRelatedGap();
362 		toolbar.addFixed( allowWildcardsCheckBox );
363 	}
364 
365 	public XmlObject createConfiguration()
366 	{
367 		XmlObjectConfigurationBuilder builder = new XmlObjectConfigurationBuilder();
368 		builder.add( "path", path );
369 		builder.add( "content", expectedContent );
370 		builder.add( "allowWildcards", allowWildcards );
371 		return builder.finish();
372 	}
373 
374 	public void selectFromCurrent()
375 	{
376 //		XmlCursor cursor = null;
377 
378 		try
379 		{
380 			XmlOptions options = new XmlOptions();
381 			options.setSavePrettyPrint();
382 			options.setSaveAggressiveNamespaces();
383 
384 			String assertableContent = getAssertable().getAssertableContent();
385 			if( assertableContent == null || assertableContent.trim().length() == 0 )
386 			{
387 				UISupport.showErrorMessage( "Missing content to select from" );
388 				return;
389 			}
390 
391 			XmlObject xml = XmlObject.Factory.parse( assertableContent );
392 
393 			String txt = pathArea == null || !pathArea.isVisible() ? getPath() : pathArea.getSelectedText();
394 			if( txt == null )
395 				txt = pathArea == null ? "" : pathArea.getText();
396 
397 			WsdlTestRunContext context = new WsdlTestRunContext( ( TestStep ) getAssertable().getModelItem() );
398 
399 			String expandedPath = PropertyExpansionUtils.expandProperties( context, txt.trim() );
400 
401 			if( contentArea != null && contentArea.isVisible() )
402 				contentArea.setText( "" );
403 
404 			XmlObject[] paths = xml.execQuery( expandedPath );
405 			if( paths.length == 0 )
406 			{
407 				UISupport.showErrorMessage( "No match in current response" );
408 			}
409 			else if( paths.length > 1 )
410 			{
411 				UISupport.showErrorMessage( "More than one match in current response" );
412 			}
413 			else
414 			{
415 				Node domNode = paths[0].getDomNode();
416 				String stringValue = null;
417 
418 				if( domNode.getNodeType() == Node.ATTRIBUTE_NODE || domNode.getNodeType() == Node.TEXT_NODE )
419 				{
420 					stringValue = domNode.getNodeValue();
421 				}
422 				else
423 				{
424 					if( domNode.getNodeType() == Node.ELEMENT_NODE )
425 					{
426 						Element elm = ( Element ) domNode;
427 						if( elm.getChildNodes().getLength() == 1 && elm.getAttributes().getLength() == 0 )
428 							stringValue = XmlUtils.getElementText( elm );
429 						else
430 							stringValue = paths[0].xmlText( options );
431 					}
432 					else
433 					{
434 						stringValue = paths[0].xmlText( options );
435 					}
436 				}
437 				
438 				if( contentArea != null && contentArea.isVisible() )
439 					contentArea.setText( stringValue );
440 				else
441 					setContent( stringValue );
442 			}
443 		}
444 		catch( Throwable e )
445 		{
446 			UISupport.showErrorMessage( e.toString() );
447 			SoapUI.logError( e );
448 		}
449 		finally
450 		{
451 //			if( cursor != null )
452 //				cursor.dispose();
453 		}
454 	}
455 
456 	public class OkAction extends AbstractAction
457 	{
458 		public OkAction()
459 		{
460 			super( "Save" );
461 		}
462 
463 		public void actionPerformed( ActionEvent arg0 )
464 		{
465 			setPath( pathArea.getText().trim() );
466 			setContent( contentArea.getText() );
467 			setAllowWildcards( allowWildcardsCheckBox.isSelected() );
468 			setConfiguration( createConfiguration() );
469 			configureResult = true;
470 			configurationDialog.setVisible( false );
471 		}
472 	}
473 
474 	public class CancelAction extends AbstractAction
475 	{
476 		public CancelAction()
477 		{
478 			super( "Cancel" );
479 		}
480 
481 		public void actionPerformed( ActionEvent arg0 )
482 		{
483 			configureResult = false;
484 			configurationDialog.setVisible( false );
485 		}
486 	}
487 
488 	public class DeclareNamespacesFromCurrentAction extends AbstractAction
489 	{
490 		public DeclareNamespacesFromCurrentAction()
491 		{
492 			super( "Declare" );
493 			putValue( Action.SHORT_DESCRIPTION, "Add namespace declaration from current message to XQuery expression" );
494 		}
495 
496 		public void actionPerformed( ActionEvent arg0 )
497 		{
498 			try
499 			{
500 				String content = getAssertable().getAssertableContent();
501 				if( content != null && content.trim().length() > 0 )
502 				{
503 					pathArea.setText( XmlUtils.declareXPathNamespaces( content ) + pathArea.getText() );
504 				}
505 				else if( UISupport.confirm( "Declare namespaces from schema instead?", "Missing Response" ) )
506 				{
507 					pathArea
508 								.setText( XmlUtils.declareXPathNamespaces( (WsdlInterface) getAssertable().getInterface() )
509 											+ pathArea.getText() );
510 				}
511 			}
512 			catch( Exception e )
513 			{
514 				log.error( e.getMessage() );
515 			}
516 		}
517 	}
518 
519 	public class TestPathAction extends AbstractAction
520 	{
521 		public TestPathAction()
522 		{
523 			super( "Test" );
524 			putValue( Action.SHORT_DESCRIPTION,
525 						"Tests the XQuery expression for the current message against the Expected Content field" );
526 		}
527 
528 		public void actionPerformed( ActionEvent arg0 )
529 		{
530 			String oldPath = getPath();
531 			String oldContent = getExpectedContent();
532 			boolean oldAllowWildcards = isAllowWildcards();
533 
534 			setPath( pathArea.getText().trim() );
535 			setContent( contentArea.getText() );
536 			setAllowWildcards( allowWildcardsCheckBox.isSelected() );
537 
538 			try
539 			{
540 				String msg = assertContent( getAssertable().getAssertableContent(), new WsdlTestRunContext( ( TestStep ) getAssertable()
541 							.getModelItem() ), "Response" );
542 				UISupport.showInfoMessage( msg, "Success" );
543 			}
544 			catch( AssertionException e )
545 			{
546 				UISupport.showErrorMessage( e.getMessage() );
547 			}
548 
549 			setPath( oldPath );
550 			setContent( oldContent );
551 			setAllowWildcards( oldAllowWildcards );
552 		}
553 	}
554 
555 	public class SelectFromCurrentAction extends AbstractAction
556 	{
557 		public SelectFromCurrentAction()
558 		{
559 			super( "Select from current" );
560 			putValue( Action.SHORT_DESCRIPTION,
561 						"Selects the XQuery expression from the current message into the Expected Content field" );
562 		}
563 
564 		public void actionPerformed( ActionEvent arg0 )
565 		{
566 			selectFromCurrent();
567 		}
568 	}
569 
570 	@Override
571 	protected String internalAssertRequest( MessageExchange messageExchange, SubmitContext context )
572 				throws AssertionException
573 	{
574 		if( !messageExchange.hasRequest( true ) )
575 			return "Missing Request";
576 		else
577 			return assertContent( messageExchange.getRequestContent(), context, "Request" );
578 	}
579 
580 	public JTextArea getContentArea()
581 	{
582 		return contentArea;
583 	}
584 
585 	public JTextArea getPathArea()
586 	{
587 		return pathArea;
588 	}
589 	
590 	public PropertyExpansion[] getPropertyExpansions()
591 	{
592 		List<PropertyExpansion> result = new ArrayList<PropertyExpansion>();
593 		
594 		result.addAll( PropertyExpansionUtils.extractPropertyExpansions( getAssertable().getModelItem(), this, "expectedContent") );
595 		result.addAll( PropertyExpansionUtils.extractPropertyExpansions( getAssertable().getModelItem(), this, "path") );
596 		
597 		return result.toArray( new PropertyExpansion[result.size()] );
598 	}
599 	
600 	public XPathReference[] getXPathReferences()
601 	{
602 		List<XPathReference> result = new ArrayList<XPathReference>();
603 			
604 		if( StringUtils.hasContent( getPath() ))
605 		{
606 			TestModelItem testStep = ( TestModelItem ) getAssertable().getModelItem();
607 			TestProperty property = testStep instanceof WsdlTestRequestStep ? testStep.getProperty( "Response" ) : testStep.getProperty( "Request" );
608 			result.add( new XPathReferenceImpl( "XQuery for " + getName() + " XQueryContainsAssertion in " + testStep.getName(), property, this, "path" ));
609 		}
610 		
611 		return result.toArray( new XPathReference[result.size()] );
612 	}
613 	
614 	public static class Factory extends AbstractTestAssertionFactory
615 	{
616 		public Factory()
617 		{
618 			super(XQueryContainsAssertion.ID, XQueryContainsAssertion.LABEL, XQueryContainsAssertion.class);
619 		}
620 	}
621 }