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