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;
14  
15  import java.util.ArrayList;
16  import java.util.Arrays;
17  import java.util.HashMap;
18  import java.util.HashSet;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Map;
22  import java.util.Set;
23  
24  import javax.wsdl.Binding;
25  import javax.wsdl.BindingOperation;
26  import javax.wsdl.Definition;
27  import javax.wsdl.Port;
28  import javax.wsdl.Service;
29  import javax.xml.namespace.QName;
30  
31  import org.apache.log4j.Logger;
32  
33  import com.eviware.soapui.SoapUI;
34  import com.eviware.soapui.config.DefinitionCacheConfig;
35  import com.eviware.soapui.config.EndpointsConfig;
36  import com.eviware.soapui.config.InterfaceConfig;
37  import com.eviware.soapui.config.OperationConfig;
38  import com.eviware.soapui.config.SoapVersionTypesConfig;
39  import com.eviware.soapui.impl.wsdl.support.soap.SoapMessageBuilder;
40  import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
41  import com.eviware.soapui.impl.wsdl.support.wsdl.CachedWsdlLoader;
42  import com.eviware.soapui.impl.wsdl.support.wsdl.UrlWsdlLoader;
43  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlContext;
44  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlLoader;
45  import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
46  import com.eviware.soapui.model.iface.Interface;
47  import com.eviware.soapui.model.iface.InterfaceListener;
48  import com.eviware.soapui.model.iface.Operation;
49  import com.eviware.soapui.settings.WsdlSettings;
50  import com.eviware.soapui.support.UISupport;
51  import com.eviware.soapui.support.types.StringList;
52  
53  /***
54   * WSDL implementation of Interface, maps to a WSDL Binding
55   * 
56   * @author Ole.Matzura
57   */
58  
59  public class WsdlInterface extends AbstractWsdlModelItem<InterfaceConfig> implements Interface
60  {
61  	public static final String STYLE_DOCUMENT = "Document";
62  	public static final String STYLE_RPC = "RPC";
63  
64     public static final String JBOSSWS_ACTIONS = "jbossws";
65     public static final String WSTOOLS_ACTIONS = "wstools";
66     public static final String XML_ACTIONS = "xml";
67  
68  	private final static Logger log = Logger.getLogger( WsdlInterface.class );
69  	
70     private List<WsdlOperation> operations = new ArrayList<WsdlOperation>();
71     private WsdlProject project;
72     private SoapMessageBuilder soapMessageBuilder;
73     private WsdlContext wsdlContext;
74     private Set<InterfaceListener> interfaceListeners = new HashSet<InterfaceListener>();
75     
76     public WsdlInterface( WsdlProject project, InterfaceConfig interfaceConfig )
77     {
78     	super( interfaceConfig, project, "/interface2.gif" );
79     	
80        this.project = project;
81        
82        if( interfaceConfig.getEndpoints() == null )
83           interfaceConfig.addNewEndpoints();
84        
85        List<OperationConfig> operationConfigs = interfaceConfig.getOperationList();
86        for (int i = 0; i < operationConfigs.size(); i++)
87        {
88           operations.add( new WsdlOperation( this, operationConfigs.get(i) ));
89        }
90        
91        for( InterfaceListener listener : SoapUI.getListenerRegistry().getListeners( InterfaceListener.class ) )
92  		{
93  			addInterfaceListener( listener );
94  		}
95     }
96  
97  	public String[] getEndpoints()
98     {
99        EndpointsConfig endpoints = getConfig().getEndpoints();
100       List<String> endpointArray = endpoints.getEndpointList();
101       return endpointArray.toArray( new String[endpointArray.size()] );
102    }
103 
104    public WsdlOperation getOperationAt(int index)
105    {
106       return operations.get(index);
107    }
108 
109    public int getOperationCount()
110    {
111       return operations.size();
112    }
113 
114    public WsdlOperation addNewOperation(BindingOperation operation)
115    {
116       WsdlOperation operationImpl = new WsdlOperation( this, getConfig().addNewOperation() );
117       operations.add( operationImpl );
118 
119       operationImpl.initFromBindingOperation( operation );
120       fireOperationAdded( operationImpl );
121       return operationImpl;
122    }
123 
124    public WsdlProject getProject()
125    {
126       return project;
127    }
128 
129    public void addEndpoint(String endpoint)
130    {
131       if( endpoint == null || endpoint.trim().length() == 0 ) return;
132       
133       endpoint = endpoint.trim();
134       String[] endpoints = getEndpoints();
135       
136       // dont add the same endpoint twice
137       if( Arrays.asList( endpoints ).contains( endpoint )) return;
138       
139       getConfig().getEndpoints().addNewEndpoint().setStringValue(endpoint);
140 		
141 		notifyPropertyChanged(ENDPOINT_PROPERTY, null, endpoint);
142    }
143    
144    public void changeEndpoint(String oldEndpoint, String newEndpoint)
145 	{
146    	if( oldEndpoint == null || oldEndpoint.trim().length() == 0 ) return;
147    	if( newEndpoint == null || newEndpoint.trim().length() == 0 ) return;
148    	
149    	EndpointsConfig endpoints = getConfig().getEndpoints();
150       
151       for( int c = 0; c < endpoints.sizeOfEndpointArray(); c++ )
152       {
153       	if( endpoints.getEndpointArray( c ).equals( oldEndpoint ) )
154       	{
155       		endpoints.setEndpointArray( c, newEndpoint );
156          	notifyPropertyChanged(ENDPOINT_PROPERTY, oldEndpoint, newEndpoint);
157          	break;
158       	}
159       }
160 	}
161 
162 	public void removeEndpoint(String endpoint)
163 	{
164 		EndpointsConfig endpoints = getConfig().getEndpoints();
165       
166       for( int c = 0; c < endpoints.sizeOfEndpointArray(); c++ )
167       {
168       	if( endpoints.getEndpointArray( c ).equals( endpoint ) )
169       	{
170       		endpoints.removeEndpoint( c );
171          	notifyPropertyChanged(ENDPOINT_PROPERTY, endpoint, null );
172          	break;
173       	}
174       }
175 	}
176 
177 	public void setDefinition( String wsdlUrl, boolean cache )
178    {
179       String old = getDefinition();
180       
181       getConfig().setDefinition( wsdlUrl );
182       
183       if( wsdlContext != null )
184       {
185       	wsdlContext.setDefinition( wsdlUrl, getConfig().getDefinitionCache() );
186       	wsdlContext.setSoapVersion( getSoapVersion() );
187       }
188       
189       notifyPropertyChanged( DEFINITION_PROPERTY, old, wsdlUrl );
190    }
191 
192 	public DefinitionCacheConfig cacheDefinition( WsdlLoader loader ) throws Exception
193 	{
194 		log.debug( "Caching definition for [" + loader.getBaseURI() + "]" );
195       if( getConfig().isSetDefinitionCache() )
196       	getConfig().unsetDefinitionCache();
197 
198 		DefinitionCacheConfig definitionCache = getConfig().addNewDefinitionCache();
199 		definitionCache.set( WsdlLoader.cacheWsdl( loader  ) );
200 		return definitionCache;
201 	}
202    
203    public String getDefinition()
204    {
205       return getConfig().isSetDefinition() ? getConfig().getDefinition() : null;
206    }
207    
208    public synchronized WsdlContext getWsdlContext()
209    {
210       if( wsdlContext == null )
211       {
212       	wsdlContext = new WsdlContext( getDefinition(), getSoapVersion(), getConfig().getDefinitionCache(), this );
213       }  
214       
215       return wsdlContext;
216    }
217    
218    /***
219     * Used by importer so we dont need to reload the context after importing..
220     * @param wsdlContext
221     */
222    
223    public void setWsdlContext( WsdlContext wsdlContext )
224    {
225    	this.wsdlContext = wsdlContext;
226    	this.wsdlContext.setSoapVersion( getSoapVersion() );
227    	
228    	if( !getConfig().isSetDefinitionCache() )
229 			getConfig().addNewDefinitionCache();
230    	
231    	if( wsdlContext.getCacheConfig() != null )
232    	{
233    		// use cache from context
234    		getConfig().setDefinitionCache( wsdlContext.getCacheConfig() );
235    	}
236    }
237 
238    public SoapMessageBuilder getMessageBuilder()
239    {
240       if( soapMessageBuilder == null )
241       {
242          try
243          {
244             soapMessageBuilder = new SoapMessageBuilder( this );
245          }
246          catch (Exception e)
247          {
248             SoapUI.logError( e );
249          }
250       }
251       return soapMessageBuilder;
252    }
253 
254    public void setSoapMessageBuilder(SoapMessageBuilder builder)
255    {
256       soapMessageBuilder = builder;
257       soapMessageBuilder.setInterface( this );
258    }
259 
260    public QName getBindingName()
261    {
262       return getConfig().getBindingName() == null ? null : QName.valueOf(getConfig().getBindingName());
263    }
264 
265    public void setBindingName(QName name)
266    {
267    	getConfig().setBindingName( name.toString() );
268    }
269    
270    public SoapVersion getSoapVersion()
271    {
272    	if( getConfig().getSoapVersion() == SoapVersionTypesConfig.X_1_2  )
273    		return SoapVersion.Soap12;
274    	
275    	return SoapVersion.Soap11;
276    }
277    
278    public void setSoapVersion( SoapVersion version )
279    {
280    	if( version == SoapVersion.Soap11 )
281    		getConfig().setSoapVersion( SoapVersionTypesConfig.X_1_1 );
282    	else if( version == SoapVersion.Soap12 )
283    		getConfig().setSoapVersion( SoapVersionTypesConfig.X_1_2 );
284    	else 
285    		throw new RuntimeException( "Unknown soapVersion [" + version + "], must be 1.1 or 1.2");
286    	
287    	getWsdlContext().setSoapVersion( version );
288    }
289 
290 	@SuppressWarnings("unchecked")
291 	public boolean updateDefinition(String url, boolean createRequests) throws Exception
292 	{
293 		WsdlContext newContext = null;
294 		
295 		if( getConfig().isSetDefinitionCache() )
296 			getConfig().unsetDefinitionCache();
297 		
298 		if( !getSettings().getBoolean( WsdlSettings.CACHE_WSDLS ) )
299 		{
300 			newContext = new WsdlContext( url, getSoapVersion(), null, null );
301 		}
302 		else
303 		{
304 			newContext = new WsdlContext( url, getSoapVersion(), null, this );	
305 		}
306 		
307 		if( !newContext.load() )
308 		{
309 			return false;
310 		}
311 		
312 		Definition definition = newContext.getDefinition();
313 		Service service = null;
314 		Port port = null;
315 		Binding binding = null;
316 		
317 		// start by finding the old binding in the new definition
318 		Map serviceMap = definition.getAllServices();
319 		Iterator<String> i = serviceMap.keySet().iterator();
320 		while( i.hasNext() )
321 		{
322 			service = (Service) serviceMap.get( i.next() );
323 			Map portMap = service.getPorts();
324 			
325 			Iterator i2 = portMap.keySet().iterator();
326 			while( i2.hasNext() )
327 			{
328 				port = (Port) portMap.get( i2.next() );
329 				if( port.getBinding().getQName().equals( getBindingName() ))
330 				{
331 					binding = port.getBinding();
332 				}
333 			}
334 			
335 			if( binding != null ) break;
336 			service = null;
337 		}
338 		
339 		if( service == null && binding == null )
340 		{
341 			binding = definition.getBinding( getBindingName() );
342 		}
343 
344 		// missing matching binding, prompt for new one to use instead (will happen if binding has been renamed)
345 		if( binding == null )
346 		{
347 			Map bindings = definition.getAllBindings();
348 			
349 			Object retval = UISupport.prompt(  
350 					"Missing matching binding [" + getBindingName() + "] in definition, select new\nbinding to map to", 
351 					"Map Binding", bindings.keySet().toArray() );
352 			
353 			if( retval == null )
354 				return false;
355 			
356 			binding = (Binding) bindings.get( retval );
357 			setBindingName( binding.getQName() );
358 		}
359 		
360 		// update name
361 		if( getSettings().getBoolean( WsdlSettings.NAME_WITH_BINDING ))
362 			setName( binding.getQName().getLocalPart() );
363 		
364 		// update context
365 		wsdlContext = newContext;
366 		if( soapMessageBuilder != null )
367 			soapMessageBuilder.setWsdlContext( wsdlContext );
368 		
369 		// prepare for transfer of operations/requests
370 		List<BindingOperation> newOperations = new ArrayList<BindingOperation>( binding.getBindingOperations() );
371 		Map<String,WsdlOperation> oldOperations = new HashMap<String,WsdlOperation>();
372 		for( int c = 0; c < operations.size(); c++ )
373 			oldOperations.put( operations.get( c ).getBindingOperationName(), operations.get( c ) );
374 		
375 		// clear existing from both collections
376 		for( int c = 0; c < newOperations.size(); c++ )
377 		{
378 			BindingOperation newOperation = newOperations.get( c );
379 			String bindingOperationName = newOperation.getName();
380 			if( oldOperations.containsKey( bindingOperationName) )
381 			{
382 				log.info( "Synchronizing existing operation [" + bindingOperationName + "]" );
383 				WsdlOperation wsdlOperation = oldOperations.get( bindingOperationName );
384 				wsdlOperation.initFromBindingOperation( newOperation );
385 				fireOperationUpdated( wsdlOperation );
386 				
387 				oldOperations.remove( bindingOperationName );
388 				newOperations.remove( c );
389 				c--;
390 			}
391 		}
392 		
393 		//	remove leftover operations
394 		i = oldOperations.keySet().iterator();
395 		while( i.hasNext())
396 		{
397 			String name = i.next();
398 			
399 			if( newOperations.size() > 0 )
400 		   {
401 				List<String> list = new ArrayList<String>();
402 				list.add( "none - delete operation" );
403 				for( int c = 0; c < newOperations.size(); c++ )
404 					list.add( newOperations.get( c ).getName() );
405 				
406 				String retval = (String) UISupport.prompt( 
407 						"Binding operation [" + name + "] not found in new interface, select new\nbinding operation to map to", 
408 						"Map Operation", list.toArray(), "none/cancel - delete operation" );
409 				
410 				int ix = retval == null ? -1 : list.indexOf( retval)-1;
411 				
412 				// delete operation?
413 				if( ix < 0 )
414 				{
415 					deleteOperation( name );
416 				}
417 				// change operation?
418 				else
419 				{
420 					BindingOperation newOperation = newOperations.get( ix );
421 					WsdlOperation wsdlOperation = oldOperations.get( name );
422 					wsdlOperation.initFromBindingOperation( newOperation );
423 					fireOperationUpdated( wsdlOperation );
424 					newOperations.remove( ix );
425 				}
426 
427 				oldOperations.remove( name );
428 		   }
429 			else 
430 			{
431 				deleteOperation( name );
432 				oldOperations.remove( name );
433 			}
434 			
435 			i = oldOperations.keySet().iterator();
436 		}
437 		
438 		// add leftover new operations
439 		if( newOperations.size() > 0 )
440 		{
441 			for( int c = 0; c < newOperations.size(); c++ )
442 			{
443 				BindingOperation newOperation = newOperations.get( c );
444 				WsdlOperation wsdlOperation = addNewOperation( newOperation );
445 				
446 				if( createRequests )
447 				{
448 					WsdlRequest request = wsdlOperation.addNewRequest( "Request 1");
449                try
450                {
451                   request.setRequestContent( wsdlOperation.createRequest( true ));
452                }
453                catch (Exception e)
454                {
455                   SoapUI.logError( e );
456                }
457 				}
458 			}
459 		}
460 		
461     	setDefinition( url, false );
462 
463 		if( port != null )
464 		{
465 			String endpoint = WsdlUtils.getSoapEndpoint( port );
466 	      if( endpoint != null )
467 	      {
468 	      	StringList list = new StringList( getEndpoints() );
469 	      	if( !list.contains( endpoint ))
470 	      	{
471 	      		if( UISupport.confirm( "Update existing requests with new endpoint\n[" + endpoint + "]", "Update Definition" ))
472 	      		{
473 	      			for( int c = 0; c < getOperationCount(); c++ )
474 	               {
475 	               	Operation operation = getOperationAt( c );
476 
477 	               	for( int ix = 0; ix < operation.getRequestCount(); ix++ )
478 	               	{
479 	               		operation.getRequestAt( ix ).setEndpoint( endpoint );
480 	               	}
481 	               }
482 	      		}
483 
484 	      		addEndpoint( endpoint );
485 	      	}
486 	      }
487 		}
488 		
489 		getProject().fireInterfaceUpdated( this );
490 		
491 		return true;
492 	}
493 
494 	private void deleteOperation(String bindingOperationName)
495 	{
496 		for( int c = 0; c < operations.size(); c++ )
497 		{
498 			WsdlOperation wsdlOperation = operations.get( c );
499 			if( wsdlOperation.getBindingOperationName().equals( bindingOperationName ))
500 			{
501 				log.info( "deleting operation [" + bindingOperationName + "]" );
502 				
503 				// remove requests first (should this be done by some listener?)
504 				while( wsdlOperation.getRequestCount() > 0 )
505 					wsdlOperation.removeRequest( (WsdlRequest) wsdlOperation.getRequestAt( 0 ));
506 				
507 				operations.remove( c );
508 
509 				try
510 				{
511 					fireOperationRemoved( wsdlOperation );
512 				}
513 				finally
514 				{
515 					wsdlOperation.release();
516 					getConfig().removeOperation( c );
517 				}
518 				
519 				return;
520 			}
521 		}
522 	}
523 	
524 	public void fireOperationAdded( WsdlOperation operation )
525    {
526       InterfaceListener[] a = interfaceListeners.toArray( new InterfaceListener[interfaceListeners.size()] );
527       
528       for (int c = 0; c < a.length; c++ )
529       {
530          a[c].operationAdded( operation );
531       }
532    }
533 	
534 	public void fireOperationUpdated( WsdlOperation operation )
535    {
536       InterfaceListener[] a = interfaceListeners.toArray( new InterfaceListener[interfaceListeners.size()] );
537       
538       for (int c = 0; c < a.length; c++ )
539       {
540          a[c].operationUpdated( operation );
541       }
542    }
543    
544    public void fireOperationRemoved( WsdlOperation operation )
545    {
546    	InterfaceListener[] a = interfaceListeners.toArray( new InterfaceListener[interfaceListeners.size()] );
547       
548       for (int c = 0; c < a.length; c++ )
549       {
550          a[c].operationRemoved( operation );
551       }
552    }
553    
554    public void fireRequestAdded( WsdlRequest request )
555    {
556    	InterfaceListener[] a = interfaceListeners.toArray( new InterfaceListener[interfaceListeners.size()] );
557       
558       for (int c = 0; c < a.length; c++ )
559       {
560          a[c].requestAdded( request );
561       }
562    }
563    
564    public void fireRequestRemoved( WsdlRequest request )
565    {
566    	InterfaceListener[] a = interfaceListeners.toArray( new InterfaceListener[interfaceListeners.size()] );
567       
568       for (int c = 0; c < a.length; c++ )
569       {
570          a[c].requestRemoved( request );
571       }
572    }
573 
574 	public void addInterfaceListener(InterfaceListener listener)
575 	{
576 		interfaceListeners.add( listener );
577 	}
578 
579 	public void removeInterfaceListener(InterfaceListener listener)
580 	{
581 		interfaceListeners.remove( listener );
582 	}
583 
584 	public WsdlOperation getOperationByName(String name)
585 	{
586 		return (WsdlOperation) getWsdlModelItemByName( operations, name );
587 	}
588 	
589 	public boolean isCached()
590 	{
591 		return getConfig().isSetDefinitionCache();
592 	}
593 
594 	public WsdlLoader createWsdlLoader()
595 	{
596 		return isCached() ? new CachedWsdlLoader( getConfig().getDefinitionCache() ) : 
597 			new UrlWsdlLoader( getDefinition() );
598 	}
599 	
600 	public void clearCache()
601 	{
602 		if( wsdlContext != null )
603 			wsdlContext.setDefinitionCache( null );
604 		
605 		if( getConfig().isSetDefinitionCache() )
606 			getConfig().unsetDefinitionCache();
607 	}
608 	
609 	public String getStyle()
610 	{
611 		if( wsdlContext == null || !wsdlContext.isLoaded() )
612 			return "<not loaded>";
613 		
614 		try
615 		{
616 			Binding binding = wsdlContext.getDefinition().getBinding( getBindingName() );
617 			if( binding == null )
618 				return "<missing binding>";
619 			
620 			if( WsdlUtils.isRpc( binding))
621 			{
622 				return STYLE_RPC;
623 			}
624 			else
625 			{
626 				return STYLE_DOCUMENT;
627 			}
628 		}
629 		catch (Exception e)
630 		{
631 			SoapUI.logError( e );
632 			return "<error>";
633 		}
634 	}
635 
636 	public void release()
637 	{
638 		super.release();
639 		
640 		for( WsdlOperation operation : operations )
641 			operation.release();
642 		
643 		interfaceListeners.clear();
644 	}
645 
646 	public List<Operation> getOperations()
647 	{
648 		return new ArrayList<Operation>( operations );
649 	}
650 
651 	@Override
652 	public void onSave()
653 	{
654 		for( WsdlOperation operation : operations )
655 			operation.onSave();
656 	}
657 }