/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.webservice; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.xsd.XSDImport; import org.eclipse.xsd.XSDSchema; import org.teiid.designer.compare.ModelProducer; import org.teiid.designer.compare.selector.ModelSelector; import org.teiid.designer.compare.selector.TransientModelSelector; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelUtil; import org.teiid.designer.core.workspace.ModelWorkspaceManager; import org.teiid.designer.metamodels.core.ModelAnnotation; import org.teiid.designer.metamodels.core.ModelType; import org.teiid.designer.metamodels.webservice.WebServicePackage; import org.teiid.designer.metamodels.wsdl.Definitions; import org.teiid.designer.metamodels.wsdl.WsdlPackage; import org.teiid.designer.metamodels.wsdl.http.HttpPackage; import org.teiid.designer.metamodels.wsdl.mime.MimePackage; import org.teiid.designer.metamodels.wsdl.soap.SoapPackage; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.bootstrap.DOMImplementationRegistry; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSSerializer; /** * @since 8.0 */ public class WebServiceModelProducer implements ModelProducer { public static final int WARNING_NO_WSDL_OBJECTS = 31301; private final IWebServiceModelBuilder builder; private final TransientModelSelector output; private final List roots; /** * @param matcherFactories * @since 4.2 */ public WebServiceModelProducer( final IWebServiceModelBuilder builder ) { this.builder = builder; this.roots = new ArrayList(); // Create a temporary model selector with the same URI as the actual relational model // (the temporary will be placed in a separate "temporary" resource set, so same URI can be used) this.output = new TransientModelSelector(builder.getModelPath().toString()); } /** * @see org.teiid.designer.compare.ModelProducer#execute(org.eclipse.core.runtime.IProgressMonitor, java.util.List) * @since 4.2 */ @Override public void execute( IProgressMonitor monitor, List problems ) throws Exception { this.roots.clear(); // Create the ModelAnnotation ... final ModelAnnotation modelAnnotation = this.output.getModelAnnotation(); if (modelAnnotation.getPrimaryMetamodelUri() != null && !modelAnnotation.getPrimaryMetamodelUri().equals(WebServicePackage.eNS_URI)) { modelAnnotation.setPrimaryMetamodelUri(WebServicePackage.eNS_URI); } if (modelAnnotation.getModelType() != null && modelAnnotation.getModelType() != ModelType.VIRTUAL_LITERAL) { modelAnnotation.setModelType(ModelType.VIRTUAL_LITERAL); } // Create the generator ... final IWebServiceGenerator generator = WebServicePlugin.createWebServiceGenerator(); generator.setWebServiceResource(this.output.getResource()); // generator.setSelectedOperations(builder.getSelectedOperations()); // Make sure that all XSD are in the workspace and are addressable ... final Set allXsdSchemas = doProcessXsds(monitor, problems); // Go through the WSDL models and obtain all root-level WSDL objects ... final Collection resources = this.builder.getWSDLResources(); final Iterator iter = resources.iterator(); while (iter.hasNext()) { final IWebServiceResource wsr = (IWebServiceResource)iter.next(); // Load the WSD ... final Resource emfResource = this.builder.getEmfResource(wsr); if (emfResource != null) { final List roots = emfResource.getContents(); final Iterator rootIter = roots.iterator(); while (rootIter.hasNext()) { final EObject root = (EObject)rootIter.next(); if (root instanceof Definitions) { generator.addWsdlDefinitions((Definitions)root); } } } } final Iterator xsdIter = allXsdSchemas.iterator(); while (xsdIter.hasNext()) { final XSDSchema schema = (XSDSchema)xsdIter.next(); generator.addXsdSchema(schema); } if (generator.getWsdlDefinitions().isEmpty()) { // No objects were found ... final String msg = WebServicePlugin.Util.getString("WebServiceModelProducer.NoWsdlObjectsFound"); //$NON-NLS-1$ problems.add(new Status(IStatus.WARNING, WebServicePlugin.PLUGIN_ID, WARNING_NO_WSDL_OBJECTS, msg, null)); } else { // At least some objects were found ... generator.generate(monitor, problems); } } /** * @see org.teiid.designer.compare.ModelProducer#getOutputSelector() * @since 4.2 */ @Override public ModelSelector getOutputSelector() { return this.output; } /** * This method is called by {@link #execute(IProgressMonitor, List)} to process the {@link IWebServiceResource} instances and * make sure that all XSDs are located in the workspace. This includes: * <ul> * <li>copying into the workspace all XSDs that are outside of the workspace</li> * <li>creating XSD files in the workspace for all XSD definitions within the WSDL files</li> * </ul> * <p> * Note that when doing so, it may be required to add to the <code>schemaLocation</code> within the XSDs. * </p> * * @param monitor * @param problems * @return the set of {@link XSDSchema} instances that should be used by the generator * @since 4.2 */ protected Set doProcessXsds( final IProgressMonitor monitor, final List problems ) { // Create the set that will be returned ... final Set results = new HashSet(); // Copy the schemas that are to be copied ... final Collection xsdDestinations = this.builder.getXsdDestinations(); final Iterator iter = xsdDestinations.iterator(); Set newXsdFiles = new HashSet(); Set destProjectRelativePaths = getDestProjectRelativePaths(xsdDestinations); while (iter.hasNext()) { final IWebServiceXsdResource xsdDest = (IWebServiceXsdResource)iter.next(); if (xsdDest == null || xsdDest.getDestinationPath() == null) { continue; } final IPath destFilePath = xsdDest.getDestinationPath(); /* * First we remove the 'project' part of the Path from the dest path all destinations include the project part of the * path for validation and display purposes. */ final IPath destProjectRelativePath = destFilePath.removeFirstSegments(1); /* * we also remove the filename from this path as we are only interested in creating the path TO the file, not the file * itself. */ final IPath destPath = destProjectRelativePath.removeLastSegments(1); /* * we must now add the 'absolute' part of the path to the destPath. The destPath in the XSD destinations is relative * to the project in the workspace. We need to add the absolute path component for the project location in order to be * able to physically create the XSD's in the proper location. */ final IPath absoluteProjectPath = builder.getParentResource().getProject().getLocation(); final IPath absoluteDestPath = absoluteProjectPath.append(destPath); /* * This call will ensure that the destination path for the XSD actually exists on the physical drive. If it does not * exist, it will create the path so that the resrource can be saved in the proper relative location. */ final File pathFile = new File(absoluteDestPath.toOSString()); pathFile.mkdirs(); try { /* * now we must refresh the project with the local file system so that the internal representations of the * resources in the project can be reflecting the physical structure on the file system that we just created (the * folders). */ builder.getParentResource().refreshLocal(IResource.DEPTH_INFINITE, null); } catch (CoreException err1) { // record a problem ... final Object[] params = new Object[] {builder.getParentResource().getName(), err1.getMessage()}; final String msg = WebServicePlugin.Util.getString("WebServiceModelProducer.ErrorRefreshingWorkspace", params); //$NON-NLS-1$ final IStatus status = new Status(IStatus.ERROR, WebServicePlugin.PLUGIN_ID, 0, msg, null); problems.add(status); } final XSDSchema schema = xsdDest.getSchema(); // Get the destination ModelResource ... final IFile file = ((IContainer)builder.getParentResource().getProject()).getFile(destProjectRelativePath); if (file == null) { // record a problem ... final Object[] params = new Object[] {xsdDest.getOriginalPath(), destProjectRelativePath}; final String msg = WebServicePlugin.Util.getString("WebServiceModelProducer.UnableToCopyXsdFrom0To1", params); //$NON-NLS-1$ final IStatus status = new Status(IStatus.ERROR, WebServicePlugin.PLUGIN_ID, 0, msg, null); problems.add(status); } else { File tempFile = null; try { // Get the DOM document for the schema ... // Comment out following line for defect 17308 Fix // schema.updateElement(true); // update deeply OutputStream stream = null; InputStream inputStream = null; try { // set import location as relative path Iterator iter1 = schema.getContents().iterator(); while (iter1.hasNext()) { Object content = iter1.next(); if (content instanceof XSDImport) { XSDImport xsdImport = ((XSDImport)content); String schemaLocation = xsdImport.getSchemaLocation(); IPath importLocation = null; if (schemaLocation != null) { importLocation = new Path(URI.createURI(xsdImport.getSchemaLocation()).path()).makeRelative(); } else { // This is a namespace import. continue; } if (destProjectRelativePaths.contains(importLocation)) { xsdImport.setSchemaLocation(ModelUtil.getRelativePath(importLocation, destProjectRelativePath)); } } } // Write the document to a temp file ... tempFile = File.createTempFile("XSDCopy", ".xsd"); //$NON-NLS-1$ //$NON-NLS-2$ stream = new FileOutputStream(tempFile); stream = new BufferedOutputStream(stream); final Element schemaElement = schema.getElement(); doWriteXmlDocument(xsdDest.getTargetNamespace(), stream, schemaElement); stream.close(); stream = null; // Read the temporary file ... inputStream = new FileInputStream(tempFile); inputStream = new BufferedInputStream(inputStream); if (file.exists()) { file.setContents(inputStream, true, true, monitor); } else { file.create(inputStream, true, monitor); } newXsdFiles.add(file); } finally { if (stream != null) { try { stream.close(); } catch (IOException err) { WebServicePlugin.Util.log(err); } } if (inputStream != null) { try { inputStream.close(); } catch (IOException err) { WebServicePlugin.Util.log(err); } } if (tempFile != null) { // RMH: this seems to work fine try { tempFile.delete(); } catch (RuntimeException err) { WebServicePlugin.Util.log(err); } } } // Obtain the underlying file and write out the DOM document ... } catch (Exception e) { final Object[] params = new Object[] {xsdDest.getOriginalPath(), destProjectRelativePath, e.getLocalizedMessage()}; final String msg = WebServicePlugin.Util.getString("WebServiceModelProducer.ErrorCopyingXsdFrom0To1", params); //$NON-NLS-1$ final IStatus status = new Status(IStatus.ERROR, WebServicePlugin.PLUGIN_ID, 0, msg, e); problems.add(status); } } } Iterator iter1 = newXsdFiles.iterator(); while (iter1.hasNext()) { IFile xsdFile = (IFile)iter1.next(); try { // Read int the new XSDSchema and register it ... final ModelResource modelResource = (ModelResource)ModelWorkspaceManager.getModelWorkspaceManager().findModelWorkspaceItem(xsdFile, true); final List allRoots = modelResource.getAllRootEObjects(); final Iterator rootIter = allRoots.iterator(); while (rootIter.hasNext()) { final EObject root = (EObject)rootIter.next(); if (root instanceof XSDSchema) { results.add(root); } } } catch (Exception e) { final String msg = WebServicePlugin.Util.getString("WebServiceModelProducer.ErrorLoadingXsd1", xsdFile); //$NON-NLS-1$ final IStatus status = new Status(IStatus.ERROR, WebServicePlugin.PLUGIN_ID, 0, msg, e); problems.add(status); } } return results; } private Set getDestProjectRelativePaths( Collection xsdDestinations ) { Set destProjectRelativePaths = new HashSet(); Iterator iter = xsdDestinations.iterator(); while (iter.hasNext()) { IWebServiceXsdResource xsdDest = (IWebServiceXsdResource)iter.next(); destProjectRelativePaths.add(xsdDest.getDestinationPath().removeFirstSegments(1)); } return destProjectRelativePaths; } /** * @param stream the stream to which the document should be written * @param schemaElement the "xsd:schema" element, which may or may not be the root of the document * @throws Exception * @since 4.2 */ protected void doWriteXmlDocument( final String targetNamespace, final OutputStream stream, final Element schemaElement ) throws Exception { // Create a copy ... // NOTE: THIS WORKS IF WE USE THE org.apache.xerces LIBRARY. // The problem is that these other implementations don't write out the // namespace declarations for the imported nodes DocumentBuilderFactory documentBuilderFactory = new DocumentBuilderFactoryImpl(); documentBuilderFactory.setNamespaceAware(true); documentBuilderFactory.setValidating(false); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); final Document docCopy = documentBuilder.newDocument(); final Element schemaElementCopy = (Element)docCopy.importNode(schemaElement, true); docCopy.appendChild(schemaElementCopy); // Namespace declarations that are only needed by VALUES of attributes or element text // will not be automatically added to the document. Therefore, some of these have to // be copied explicitly. final NamedNodeMap nameMap = schemaElement.getOwnerDocument().getDocumentElement().getAttributes(); for (int i = 0; i < nameMap.getLength(); i++) { final Node child = nameMap.item(i); final String value = child.getNodeValue(); // final String name = child.getLocalName(); final String prefix = child.getPrefix(); // System.out.println(" attribute " + (prefix != null ? (prefix + ":") : "" )+ name + "=" + value ); if ("xmlns".equals(prefix)) { //$NON-NLS-1$ if (WsdlPackage.eNS_URI.equals(value) || SoapPackage.eNS_URI.equals(value) || HttpPackage.eNS_URI.equals(value) || MimePackage.eNS_URI.equals(value)) { // skip these namespace declarations; assume WSDL and it's associated namespaces aren't used in VALUES } else { // Make a copy of this namespace declaration and add to the schema ... final Node newChild = docCopy.importNode(child, false); schemaElementCopy.setAttributeNode((Attr)newChild); // final Attr attr = docCopy.createAttribute(prefix + ':' + name); // attr.setValue(value); // schemaElementCopy.setAttributeNode(attr); } } } // Write it out ... DOMImplementationLS impl = (DOMImplementationLS)DOMImplementationRegistry.newInstance().getDOMImplementation("LS"); //$NON-NLS-1$ LSSerializer writer = impl.createLSSerializer(); LSOutput output = impl.createLSOutput(); output.setByteStream(stream); output.setEncoding("UTF-8"); //$NON-NLS-1$ writer.write(docCopy, output); } }