/** * EasySOA Proxy * Copyright 2011-2013 Open Wide * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Contact : easysoa-dev@googlegroups.com */ package org.easysoa.registry.dbb; import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import javax.xml.namespace.QName; import org.apache.log4j.Logger; import org.easysoa.registry.facets.InformationServiceDataFacet; import org.easysoa.registry.facets.WsdlInfoFacet; import org.easysoa.registry.types.Endpoint; import org.easysoa.registry.types.InformationService; import org.easysoa.registry.types.OperationInformation; import org.easysoa.registry.types.ResourceDownloadInfo; import org.easysoa.registry.types.ServiceImplementation; import org.easysoa.registry.types.SoaModelSerializationUtil; import org.easysoa.registry.types.ids.InformationServiceName; import org.easysoa.registry.types.ids.ServiceImplementationName; import org.easysoa.registry.types.ids.ServiceNameType; import org.easysoa.registry.wsdl.WsdlBlob; import org.nuxeo.ecm.core.api.Blob; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.model.PropertyException; import org.ow2.easywsdl.schema.api.Element; import org.ow2.easywsdl.wsdl.api.Description; import org.ow2.easywsdl.wsdl.api.Operation; import org.ow2.easywsdl.wsdl.api.Part; import org.ow2.easywsdl.wsdl.api.abstractItf.AbsItfParam; /** * Extracts and sets metadata from WSDL. Tries to do it only if (doc, file name, content) actually changed. * * NB. if called on event, can't be on beforeDocumentModification because digest not yet computed at storage level. * * About WSDL parsing implementation architecture : * see Thumbnail https://github.com/ldoguin/nuxeo-thumbnail http://www.nuxeo.com/blog/development/2012/06/monday-dev-heaven-adding-thumbnail-preview-document-content-nuxeo/ * * @author jguillemotte */ public class WsdlResourceParsingServiceImpl implements ResourceParsingService { private static Logger logger = Logger.getLogger(WsdlResourceParsingServiceImpl.class); public static final String SOAP_CONTENT_TYPE = "application/soap+xml"; // ; charset=utf-8 http://www.w3schools.com/soap/soap_httpbinding.asp /** * Default constructor */ public WsdlResourceParsingServiceImpl(){} /** * * @param documentType * @return */ @Override public boolean isWsdlInfo(DocumentModel soaNodeDocModel) { return soaNodeDocModel.getFacets().contains(WsdlInfoFacet.FACET_WSDLINFO); // rather than wsdlDocumentTypes.contains(model.getType()) } /** * check if the document model is a WSDL resource * @param model * @return */ @Override public boolean isWsdlFileResource(DocumentModel soaNodeDocModel){ // isRDI && isWsdl(i.e. isWsdlResource(from type or name of file and / or url) || isWsdlHardType i.e. iserv && endpoint) if((isRDI(soaNodeDocModel) && isWsdl(soaNodeDocModel)) || isWsdlHardType(soaNodeDocModel)){ return true; } return false; } /** * Check if the document is a RDI (ResourceDownloadInfo) document * @param model The model to check * @return true if the model is a RDI document */ private boolean isRDI(DocumentModel model){ return model.getFacets().contains(ResourceDownloadInfo.FACET_RESOURCEDOWNLOADINFO); } /** * Check if the model is a WSDL document * @param model The model to check * @return true if the model is a wsdl document */ private boolean isWsdl(DocumentModel model){ // get the file // or get the url try { String url = (String)model.getPropertyValue(ResourceDownloadInfo.XPATH_URL); Blob blob = (Blob)model.getPropertyValue("file:content"); // Check not null if(blob == null){ return false; } if(url == null || url.length() == 0){ return false; } // Check wsdl if(blob.getFilename().toLowerCase().endsWith(".wsdl") || url.toLowerCase().contains("?wsdl")){ return true; } return false; } catch(Exception ex){ } return false; } /** * Return tru if the model is an InformationService or an Endpoint * @param model The model to check * @return true if the model is an InformationService or an Endpoint */ private boolean isWsdlHardType(DocumentModel model){ HashSet<String> wsdlFileDocumentTypes = new HashSet<String>(3); wsdlFileDocumentTypes.add(InformationService.DOCTYPE); wsdlFileDocumentTypes.add(Endpoint.DOCTYPE); return wsdlFileDocumentTypes.contains(model.getType()); } /** * * @param sourceDocument * @return * @throws ClientException */ @Override public boolean extractMetas(DocumentModel sourceDocument) throws ClientException { boolean documentModified = false; // getting new info : WsdlBlob wsdlBlob = new WsdlBlob(sourceDocument); // NB. previousDocumentModel can't be used to get old digest if (wsdlBlob.hasFileChangedIfAny()) { if (wsdlBlob.getWsdl() != null) { // try extracting from parsing documentModified = parseResourceAndSetMetas(wsdlBlob); } else { // If none anymore, reinit all extracted metadata (or remove facet) String oldWsdlFileName = (String) sourceDocument.getPropertyValue(WsdlInfoFacet.XPATH_WSDL_FILE_NAME); if (oldWsdlFileName != null) { documentModified = resetMetasIfChanged(sourceDocument); } // Try extracting metadata from soaname if (isWsdlInfo(sourceDocument)) { // NB. additionally also if serviceimpl documentModified = extractMetasFromSoaName(sourceDocument) || documentModified; } } } return documentModified; } /** * TODO in AbstractResourceService ?! * @param wsdlBlob * @return * @throws ClientException */ protected boolean parseResourceAndSetMetas(WsdlBlob wsdlBlob) throws ClientException { boolean documentModified = false; // update filename if required if (wsdlBlob.hasFileNameChanged()) { wsdlBlob.getSourceDocument().setPropertyValue(WsdlInfoFacet.XPATH_WSDL_FILE_NAME, wsdlBlob.getBlob().getFilename()); documentModified = true; } if (wsdlBlob.hasContentChanged()) { // checking in case just filename changed documentModified = extractMetadataFromWsdl(wsdlBlob.getWsdl(), wsdlBlob.getSourceDocument()) || documentModified; //sourceDocument.setPropertyValue("wsdl:wsdlFileDigest", newDigest); } return documentModified; } /** * Extracts relevant metadata * @param wsdl * @param sourceDocument * @return * @throws ClientException */ private static boolean extractMetadataFromWsdl(Description wsdl, DocumentModel sourceDocument) throws ClientException { if (wsdl != null) { // for now, only taking the first Interface & Service of the WSDL. // TODO LATER develop WSDL import that creates as many services as there are in the WSDL // and / or allow to select the right one //String wsdlDoc = wsdl.getDocumentation().getContent();//TODO ?!? QName portTypeName = wsdl.getInterfaces().get(0).getQName(); QName serviceName = wsdl.getServices().get(0).getQName(); // TODO better : make sure is is of the interface above String wsdlVersion = wsdl.getVersion().name(); List<OperationInformation> operationInfos = new ArrayList<OperationInformation>(); for (Operation wsdlOperation : wsdl.getInterfaces().get(0).getOperations()) { StringBuffer docBuf = new StringBuffer(); if (wsdlOperation.getDocumentation() != null) { docBuf.append("\n"); docBuf.append(wsdlOperation.getDocumentation().getContent()); } String inParameters = getParameters(wsdlOperation.getInput(), docBuf); String outParameters = getParameters(wsdlOperation.getOutput(), docBuf); //TODO LATER also error message OperationInformation operationInfo = new OperationInformation( wsdlOperation.getQName().getLocalPart(),//TODO LATER rather toString() and only display localPart inParameters, outParameters, docBuf.toString(), SOAP_CONTENT_TYPE, SOAP_CONTENT_TYPE); operationInfos.add(operationInfo); } boolean changed = setWsdlMetadataIfChanged(sourceDocument, wsdlVersion, portTypeName, serviceName, operationInfos); return changed; } return false; } /** * * @param itfParam input, output or fault * @return */ private static String getParameters(AbsItfParam itfParam, StringBuffer docBuf) { if (itfParam != null) { if (!itfParam.getParts().isEmpty()) { Part part = itfParam.getParts().get(0); //TODO LATER multipart if (part != null) { if (part.getElement() != null) { Element element = part.getElement(); if (element.getDocumentation() != null) { docBuf.append("\n"); docBuf.append(element.getDocumentation().getContent()); } if (element.getType().getDocumentation() != null) { docBuf.append("\n"); docBuf.append(element.getType().getDocumentation().getContent()); } //TODO also of all inner elements... // NB. inputElement and not Type, because if anonymous Type its QName will be null return element.getQName().getLocalPart(); //TODO LATER rather toString() and only display localPart; } } } // else no part, seen in ContactSvc.asmx.wsdl : // <wsdl:operation name="getRepartitionTypeStructure"> // <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Get repartition TypeStructure</wsdl:documentation> // <wsdl:input message="" /> // <wsdl:output message="tns:getRepartitionTypeStructureSoapOut" /> // </wsdl:operation> } return ""; } private static boolean setWsdlMetadataIfChanged(DocumentModel sourceDocument, String wsdlVersion, QName portTypeName, QName serviceName, List<OperationInformation> operationInfos) throws ClientException { boolean changed = false; if (InformationService.DOCTYPE.equals(sourceDocument.getType())) { if (false) { // TODO [soaname change] ?!? sourceDocument.setPropertyValue(InformationService.XPATH_SOANAME, new InformationServiceName(ServiceNameType.WEB_SERVICE, '{' + portTypeName.getNamespaceURI() + '}' + portTypeName.getLocalPart()).toString()); changed = true; } changed = setIfChanged(sourceDocument, InformationService.XPATH_WSDL_SERVICE_NAME, toStringIfNotNull(serviceName)) || changed; changed = setIfChanged(sourceDocument, InformationService.XPATH_WSDL_VERSION, wsdlVersion) || changed; changed = setIfChanged(sourceDocument, InformationService.XPATH_WSDL_PORTTYPE_NAME, toStringIfNotNull(portTypeName)) || changed; changed = setIfChanged(sourceDocument, InformationServiceDataFacet.XPATH_OPERATIONS, SoaModelSerializationUtil.operationInformationToPropertyValue(operationInfos)) || changed; } else { // Endpoint.DOCTYPE.equals(sourceDocument.getType()) // NB. for now WSDL metas of all model elements (InformationService, (ServiceImplementation), Enndpoint) are the same changed = setIfChanged(sourceDocument, Endpoint.XPATH_WSDL_PORTTYPE_NAME, toStringIfNotNull(portTypeName)) || changed; changed = setIfChanged(sourceDocument, Endpoint.XPATH_WSDL_SERVICE_NAME, toStringIfNotNull(serviceName)) || changed; changed = setIfChanged(sourceDocument, Endpoint.XPATH_WSDL_VERSION, wsdlVersion) || changed; //changed = setIfChanged(sourceDocument, "iserv:operations", operationInfos) || changed; //TODO LATER also operations on Endpoint ?? } return changed; } /** * TODO in AbstractResourceService ?! * @param sourceDocument * @return * @throws ClientException */ protected boolean resetMetasIfChanged(DocumentModel sourceDocument) throws ClientException { boolean changed = setWsdlMetadataIfChanged(sourceDocument, null, null, null, null); changed = setIfChanged(sourceDocument, WsdlInfoFacet.XPATH_WSDL_FILE_NAME, null) || changed; return changed; } private static Serializable toStringIfNotNull(Object o) { if (o == null) { return null; } return o.toString(); } private static boolean setIfChanged(DocumentModel doc, String prop, Serializable newValue) throws PropertyException, ClientException { Serializable oldValue = doc.getPropertyValue(prop); if (newValue == null && oldValue != null || newValue != null && !newValue.equals(oldValue)) { doc.setPropertyValue(prop, newValue); return true; } return false; } /** * TODO in AbstractResourceService ?! * @param sourceDocument * @return * @throws ClientException */ private boolean extractMetasFromSoaName(DocumentModel sourceDocument) throws ClientException { boolean documentModified = false; if (InformationService.DOCTYPE.equals(sourceDocument.getType())) { if (sourceDocument.getPropertyValue(InformationService.XPATH_WSDL_PORTTYPE_NAME) == null) { InformationServiceName parsedSoaName = InformationServiceName.fromName( (String) sourceDocument.getPropertyValue(InformationService.XPATH_SOANAME)); if (parsedSoaName != null && parsedSoaName.getType().equals(ServiceNameType.WEB_SERVICE)) { // NB. not null only if in format ws/java:ns:name String portTypeName = parsedSoaName.getInterfaceName(); sourceDocument.setPropertyValue(InformationService.XPATH_WSDL_PORTTYPE_NAME, portTypeName); documentModified = true; } } } else if (ServiceImplementation.DOCTYPE.equals(sourceDocument.getType())) { ServiceImplementationName parsedSoaName = ServiceImplementationName.fromName( (String) sourceDocument.getPropertyValue(ServiceImplementation.XPATH_SOANAME)); if (parsedSoaName != null && parsedSoaName.getType().equals(ServiceNameType.WEB_SERVICE)) { if (sourceDocument.getPropertyValue(ServiceImplementation.XPATH_WSDL_PORTTYPE_NAME) == null) { String portTypeName = parsedSoaName.getInterfaceName(); sourceDocument.setPropertyValue(ServiceImplementation.XPATH_WSDL_PORTTYPE_NAME, portTypeName); documentModified = true; } /*if (sourceDocument.getPropertyValue(ServiceImplementation.XPATH_WSDL_SERVICE_NAME) == null) { String serviceName = parsedSoaName.getImplementationName(); // NO rather {ns}implementationLocalName sourceDocument.setPropertyValue(ServiceImplementation.XPATH_WSDL_SERVICE_NAME, serviceName); documentModified = true; }*/ } } // NB. not done for endpoint whose SOA name is always "environment:url" return documentModified; } }