/****************************************************************************** * Copyright (c) 2008-2013, Linagora * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Linagora - initial API and implementation *******************************************************************************/ package com.ebmwebsourcing.petals.common.internal.provisional.utils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.wsdl.Definition; import javax.wsdl.Operation; import javax.wsdl.Port; import javax.wsdl.PortType; import javax.wsdl.Service; import javax.wsdl.WSDLException; import javax.wsdl.extensions.soap.SOAPAddress; import javax.wsdl.extensions.soap.SOAPBinding; import javax.wsdl.extensions.soap12.SOAP12Address; import javax.wsdl.extensions.soap12.SOAP12Binding; import javax.wsdl.xml.WSDLWriter; import javax.xml.namespace.QName; import org.eclipse.bpel.common.wsdl.helpers.UriAndUrlHelper; import org.eclipse.core.runtime.IStatus; import com.ebmwebsourcing.petals.common.generation.Mep; import com.ebmwebsourcing.petals.common.internal.PetalsCommonPlugin; /** * A WSDL parser for the version 1.1 of the WSDL specification. * @author Vincent Zurczak - EBM WebSourcing */ public final class WsdlUtils { /** * The unique instance of this parser. */ public static final WsdlUtils INSTANCE = new WsdlUtils(); /** * The instance of the WSDL parser for the version 1.1 of WSDL. */ private javax.wsdl.xml.WSDLReader wsdlReader11; /** * The factory to write WSDL files. */ private javax.wsdl.factory.WSDLFactory wsdlFactory11; /** * The constructor initializes the two parsers. */ private WsdlUtils() { try { this.wsdlFactory11 = javax.wsdl.factory.WSDLFactory.newInstance(); this.wsdlReader11 = this.wsdlFactory11.newWSDLReader(); this.wsdlReader11.setFeature( "javax.wsdl.verbose", false ); //$NON-NLS-1$ } catch( javax.wsdl.WSDLException e ) { PetalsCommonPlugin.log( e, IStatus.WARNING ); } } /** * Parses a WSDL URL, given as a string (WSDL 1.1 only). * @param wsdlUri the URI of the WSDL to parse (not null). * @return a list of beans containing easily accessible data * @throws IllegalArgumentException if the URI is invalid * @throws InvocationTargetException if the WSDL is not a valid WSDL 1.1 * <p> * An Eclipse log entry is also created in this case. * </p> */ public List<JbiBasicBean> parse( String wsdlUrlAsString ) throws IllegalArgumentException, InvocationTargetException { URI uri = UriAndUrlHelper.urlToUri( wsdlUrlAsString ); return parse( uri ); } /** * Parses a WSDL URL (WSDL 1.1 only). * @param wsdlUri the URI of the WSDL to parse * @return a list of {@link JbiBasicBean}s (never null) * @throws InvocationTargetException if the WSDL is not a valid WSDL 1.1 * <p> * An Eclipse log entry is also created in this case. * </p> */ public List<JbiBasicBean> parse( URI wsdlUri ) throws InvocationTargetException { List<JbiBasicBean> result = new ArrayList<JbiBasicBean> (); // Read WSDL 1.1 definitions from the URL try { Definition def = this.wsdlReader11.readWSDL( wsdlUri.toString()); for( Iterator<?> itService = def.getServices().keySet().iterator(); itService.hasNext(); ) { QName serviceName = (QName) itService.next(); Object value = def.getServices().get( serviceName ); if( !( value instanceof javax.wsdl.Service )) continue; Service service = (javax.wsdl.Service) value; Map<?,?> ports = service.getPorts(); for( Map.Entry<?,?> entry : ports.entrySet()) { String portName = (String) entry.getKey(); Port port = (Port) entry.getValue(); List<?> l = port.getExtensibilityElements(); for( Object o : l ) { if( o == null ) continue; JbiBasicBean bean = new JbiBasicBean(); String soapAddress; if( o instanceof SOAPAddress ) soapAddress = ((SOAPAddress) o).getLocationURI(); else if( o instanceof SOAP12Address ) { soapAddress = ((SOAP12Address) o).getLocationURI(); bean.setSoapVersion( SoapVersion.v12 ); } else continue; bean.setServiceName( service.getQName()); bean.setSoapAddress( soapAddress ); bean.setEndpointName( portName ); PortType p = port.getBinding().getPortType(); bean.setInterfaceName( p.getQName()); // Bug PETALSSTUD-71: make sure the referenced port type really exists in the WSDL bean.setPortTypeExists( ! p.isUndefined()); // Bug PETALSSTUD-71 // Get the operations if( p.getOperations() != null ) { for( Object opo : p.getOperations()) { if( opo instanceof Operation ) { QName operationName = new QName( p.getQName().getNamespaceURI(), ((Operation) opo).getName()); Mep mep = Mep.IN_OUT; if(((Operation) opo).isUndefined()) mep = Mep.UNKNOWN; // FIXME: it does not work. Tested with Talend#executeJobOnly. else if(((Operation) opo).getOutput() == null || ((Operation) opo).getOutput().getMessage() == null || ((Operation) opo).getOutput().getMessage().isUndefined()) mep = Mep.IN_ONLY; bean.addOperation( operationName, mep ); } } } result.add( bean ); } } } } catch( WSDLException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); throw new InvocationTargetException( e ); } return result; } /** * Updates a WSDL file by changing the end-point (port name). * * @param file the WSDL file to parse * @param serviceName the name of the service whose end-point (port name) must be updated * @param the old port name * @param newEndpoint the new end-point * @return true if the update worked, false otherwise */ public boolean updateEndpointNameInWsdl( File wsdlFile, QName serviceName, String oldPortName, String newEndpoint ) { return updateEndpointAndServiceNamesInWsdl( wsdlFile, serviceName, null, oldPortName, newEndpoint ); } /** * Updates a WSDL file by changing the end-point (port name) and service name. * * @param file the WSDL file to parse * @param serviceName the name of the service whose end-point (port name) and name must be updated * @param newServiceName the new service name * @param the old port name (can be null if it is not known) * @param newEndpoint the new end-point * @return true if the update worked, false otherwise */ public boolean updateEndpointAndServiceNamesInWsdl( java.io.File wsdlFile, QName serviceName, QName newServiceName, String oldPortName, String newEndpoint ) { boolean updated = false; try { // Parse the WSDL to update Definition def = this.wsdlReader11.readWSDL( wsdlFile.toURI().toString()); Object value = def.getServices().get( serviceName ); if( !( value instanceof Service )) throw new IOException( "Invalid service properties (not found or invalid class)." ); Service service = (Service) value; Map<?,?> ports = service.getPorts(); Set<?> keyPorts = ports.keySet(); // Find the elements to update Port rightPort = null; portsLoop: for( Object name : keyPorts ) { String portName = (String) name; Port port = (Port) ports.get( portName ); for( Object o : port.getBinding().getExtensibilityElements()) { boolean isSoapBinding = o instanceof SOAPBinding || o instanceof SOAP12Binding; boolean isRightPort = oldPortName == null || port.getName().equals( oldPortName ); if( isSoapBinding && isRightPort ) { rightPort = port; break portsLoop; } } } // Update the end-point and service if( rightPort != null ) { rightPort.setName( newEndpoint ); if( newServiceName != null ) service.setQName( newServiceName ); WSDLWriter writer = this.wsdlFactory11.newWSDLWriter(); FileOutputStream fos = null; try { fos = new FileOutputStream( wsdlFile ); writer.writeWSDL( def, fos ); } finally { if( fos != null ) fos.close(); } updated = true; } } catch( WSDLException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } catch( IOException e ) { PetalsCommonPlugin.log( e, IStatus.ERROR ); } return updated; } /** * Gets the operations of the WSDL service identified by the parameters. * @param wsdlUri the URI of the WSDL to parse * @param itfName the interface name * @param itfNs * @param srvName the service name * @param srvNs * @param edptName the end-point name * @return a map (operation name = operation MEP), never null */ public Map<QName, Mep> getOperations( URI wsdlUri, String itfName, String itfNs, String srvName, String srvNs, String edptName ) { Map<QName,Mep> result = new HashMap<QName,Mep> (); try { for( JbiBasicBean bean : parse( wsdlUri )) { if( bean.haveSameIdentifiers( itfName, itfNs, srvName, srvNs, edptName )) { result.putAll( bean.getOperations()); break; } } } catch( Exception e ) { // nothing } return result; } /** * A class use to store WSDL parsing results. */ public static class JbiBasicBean { private QName itfName, srvName; private String endpointName; private String soapAddress; private boolean portTypeExists; private SoapVersion soapVersion = SoapVersion.v11; private final Map<QName, Mep> operations = new HashMap<QName,Mep> (); /** * @return the itfName */ public QName getInterfaceName() { return this.itfName; } /** * @param itfName the itfName to set */ public void setInterfaceName( QName itfName ) { this.itfName = itfName; } /** * @return the srvName */ public QName getServiceName() { return this.srvName; } /** * @param srvName the srvName to set */ public void setServiceName( QName srvName ) { this.srvName = srvName; } /** * @return the portTypeExists */ public boolean isPortTypeExists() { return this.portTypeExists; } /** * @param portTypeExists the portTypeExists to set */ public void setPortTypeExists( boolean portTypeExists ) { this.portTypeExists = portTypeExists; } /** * @return the endpointName */ public String getEndpointName() { return this.endpointName; } /** * @param endpointName the endpointName to set */ public void setEndpointName( String endpointName ) { this.endpointName = endpointName; } /** * @return the soapAddress */ public String getSoapAddress() { return this.soapAddress; } /** * @param soapAddress the soapAddress to set */ public void setSoapAddress( String soapAddress ) { this.soapAddress = soapAddress; } /** * @return the soapVersion */ public SoapVersion getSoapVersion() { return this.soapVersion; } /** * @param soapVersion the soapVersion to set */ public void setSoapVersion( SoapVersion soapVersion ) { this.soapVersion = soapVersion; } /** * @return the operations */ public Map<QName,Mep> getOperations() { return this.operations; } /** * @param operation an operation name * @param mep the operation's MEP * @see java.util.List#add(java.lang.Object) */ public void addOperation( QName operation, Mep mep ) { this.operations.put( operation, mep ); } /** * @param itfName * @param itfNs * @param srvName * @param srvNs * @param edptName * @return true only if this bean has the same identifiers than those given in parameters */ public boolean haveSameIdentifiers( String itfName, String itfNs, String srvName, String srvNs, String edptName ) { return CommonUtils.areEqual( itfName, this.itfName.getLocalPart()) && CommonUtils.areEqual( itfNs, this.itfName.getNamespaceURI()) && CommonUtils.areEqual( srvName, this.srvName.getLocalPart()) && CommonUtils.areEqual( srvNs, this.srvName.getNamespaceURI()) && CommonUtils.areEqual( edptName, this.endpointName ); } } /** * The SOAP version. */ public static enum SoapVersion { v11, v12; @Override public String toString() { switch( this ) { case v11: return "1.1"; default: return "1.2"; } }; } }