/* * 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.vdb; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.UnmarshalException; import javax.xml.bind.Unmarshaller; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.emf.ecore.resource.Resource; import org.teiid.core.designer.util.ChecksumUtil; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.core.designer.util.FileUtils; import org.teiid.core.designer.util.OperationUtil; import org.teiid.core.designer.util.OperationUtil.Unreliable; import org.teiid.core.designer.util.StringConstants; import org.teiid.core.designer.util.StringUtilities; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.container.ResourceFinder; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelUtil; import org.teiid.designer.core.workspace.WorkspaceResourceFinderUtil; import org.teiid.designer.metamodels.core.ModelType; import org.teiid.designer.roles.DataRole; import org.teiid.designer.runtime.spi.ITeiidVdb; import org.teiid.designer.runtime.version.spi.ITeiidServerVersion; import org.teiid.designer.runtime.version.spi.TeiidServerVersion; import org.teiid.designer.runtime.version.spi.TeiidServerVersion.Version; import org.teiid.designer.vdb.Vdb.Xml; import org.teiid.designer.vdb.file.ValidationVersionCallback; import org.teiid.designer.vdb.file.VdbFileProcessor; import org.teiid.designer.vdb.manifest.EntryElement; import org.teiid.designer.vdb.manifest.MetadataElement; import org.teiid.designer.vdb.manifest.ModelElement; import org.teiid.designer.vdb.manifest.ProblemElement; import org.teiid.designer.vdb.manifest.PropertyElement; import org.teiid.designer.vdb.manifest.Severity; import org.teiid.designer.vdb.manifest.SourceElement; import org.teiid.designer.vdb.manifest.VdbElement; import org.xml.sax.SAXException; /** * Utility methods used to query VDB manifest and VDB's * * @since 8.0 */ public class VdbUtil implements VdbConstants { @SuppressWarnings( "javadoc" ) public static final String PHYSICAL = "PHYSICAL"; //$NON-NLS-1$ @SuppressWarnings( "javadoc" ) public static final String VIRTUAL = "VIRTUAL"; //$NON-NLS-1$ @SuppressWarnings( "javadoc" ) public static final String FUNCTION = "FUNCTION"; //$NON-NLS-1$ @SuppressWarnings( "javadoc" ) public static final String OTHER = "OTHER"; //$NON-NLS-1$ @SuppressWarnings( "javadoc" ) public static final String DEPRECATED_TYPE = "TYPE"; //$NON-NLS-1$ @SuppressWarnings( "javadoc" ) public static final String XML_EXTENSION = "XML"; //$NON-NLS-1$ /** * @param theVdb * @return list of vdb model files */ public static Collection<IFile> getVdbModels( Vdb theVdb ) { Collection<IFile> iFiles = new ArrayList<IFile>(); for (VdbEntry modelEntry : theVdb.getModelEntries()) { IResource resource = ModelerCore.getWorkspace().getRoot().findMember(modelEntry.getPath()); // if resource has been moved in the workspace since being added to the VDB then it will not be found if ((resource != null) && resource.exists()) { iFiles.add((IFile)resource); } } return iFiles; } /** * @param file * @return preview attribute value for VDB. true or false * @throws Exception */ public static boolean isPreviewVdb( final IFile file ) throws Exception { CoreArgCheck.isNotNull(file, "file is null"); //$NON-NLS-1$ if (file.exists()) { // if VDB file is empty just check file name if (file.getLocation().toFile().length() == 0L) { // make sure file prefix and extension is right if (!ITeiidVdb.VDB_EXTENSION.equals(file.getFileExtension())) { return false; } return file.getName().startsWith(VdbConstants.PREVIEW_PREFIX); } VdbElement manifest = VdbUtil.getVdbManifest(file); if (manifest != null) { // VDB properties for (final PropertyElement property : manifest.getProperties()) { final String name = property.getName(); if (Xml.PREVIEW.equals(name)) { return Boolean.parseBoolean(property.getValue()); } } } } return false; } /** * @param file * @return preview attribute value for VDB. true or false */ public static boolean isDynamicVdb( final IFile file ) { CoreArgCheck.isNotNull(file, "file is null"); //$NON-NLS-1$ boolean result = false; if (file.exists()) { // if VDB file is empty just check file name if (file.getLocation().toFile().length() > 0) { // make sure file extension is right if (! XML_EXTENSION.equalsIgnoreCase(file.getFileExtension())) { return false; } VdbElement manifest = null; try { manifest = VdbUtil.getVdbManifest(file.getLocation().toFile()); } catch (Exception ex) { result = false; //VdbPlugin.UTIL.log(IStatus.ERROR, ex, "Problem loading VDB manifest for VDB = " + file.getName()); } if (manifest != null) { result = true; } } } return result; } /** * @param file * @return preview attribute value for VDB. true or false */ public static boolean isDdlVdb( final IFile file ) { CoreArgCheck.isNotNull(file, "file is null"); //$NON-NLS-1$ boolean result = false; if (file.exists()) { // if VDB file is empty just check file name if (file.getLocation().toFile().length() > 0) { // make sure file prefix and extension is right if ( ! ITeiidVdb.VDB_EXTENSION.equalsIgnoreCase(file.getFileExtension())) { return false; } VdbElement manifest = null; try { manifest = VdbUtil.getVdbManifest(file); } catch (Exception ex) { VdbPlugin.UTIL.log(ex); } if (manifest != null) { for( ModelElement model : manifest.getModels() ) { List<MetadataElement> allMetadata = model.getMetadata(); if( allMetadata != null) { for( MetadataElement metadata :allMetadata ) { if( metadata.getType().equalsIgnoreCase("DDL-FILE") ) { result = true; break; } } } if( result ) break; } } } } return result; } /** * Utility method to determine if a vdb contains models of a certain "class" * @param file * * @param modelClass * @param type * @return preview attribute value for VDB. true or false * @throws Exception */ public static boolean hasModelClass(final IFile file, final String modelClass, final String type) throws Exception { if (file.exists() && ITeiidVdb.VDB_EXTENSION.equals(file.getFileExtension())) { VdbElement manifest = VdbUtil.getVdbManifest(file); if (manifest != null) { for (ModelElement model : manifest.getModels()) { String typeValue = model.getType(); if (type.equalsIgnoreCase(typeValue)) { for (final PropertyElement property : model.getProperties()) { final String name = property.getName(); if (ModelElement.MODEL_CLASS.equals(name)) { String modelClassValue = property.getValue(); if (modelClass.equalsIgnoreCase(modelClassValue)) { return true; } } } } } } } return false; } /** * Utility method to extract a copy of a VDB zip file's vdb.xml in VDB element xml structure * @param file * @return the root VdbElement * @throws Exception */ public static VdbElement getVdbManifest( final IFile file ) throws Exception { final VdbElement[] manifest = new VdbElement[1]; if (!file.exists()) { return null; } if( ModelUtil.isVdbArchiveFile(file) ) { try { OperationUtil.perform(new Unreliable() { ZipFile archive = null; InputStream entryStream = null; @Override public void doIfFails() { } @Override public void finallyDo() throws Exception { if (entryStream != null) entryStream.close(); if (archive != null) archive.close(); } @Override public void tryToDo() throws Exception { archive = new ZipFile(file.getLocation().toString()); boolean foundManifest = false; for (final Enumeration<? extends ZipEntry> iter = archive.entries(); iter.hasMoreElements();) { final ZipEntry zipEntry = iter.nextElement(); entryStream = archive.getInputStream(zipEntry); if (zipEntry.getName().equals(MANIFEST)) { // Initialize using manifest foundManifest = true; final Unmarshaller unmarshaller = getJaxbContext().createUnmarshaller(); unmarshaller.setSchema(getManifestSchema()); manifest[0] = (VdbElement)unmarshaller.unmarshal(entryStream); } // Don't process any more than we need to. if (foundManifest) { break; } } } }); } catch (Exception ex) { VdbPlugin.UTIL.log(ex); return null; } return manifest[0]; } if( file.getFileExtension() != null && file.getFileExtension().equalsIgnoreCase("XML") ) { try { OperationUtil.perform(new Unreliable() { InputStream fileStream = null; @Override public void doIfFails() { } @Override public void finallyDo() throws Exception { if (fileStream != null) fileStream.close(); } @Override public void tryToDo() throws Exception { fileStream = new FileInputStream(file.getLocation().toFile()); final Unmarshaller unmarshaller = getJaxbContext().createUnmarshaller(); unmarshaller.setSchema(getManifestSchema()); manifest[0] = (VdbElement)unmarshaller.unmarshal(fileStream); } }); } catch (Exception ex) { VdbPlugin.UTIL.log(ex); return null; } return manifest[0]; } return null; } /** * @param dynamicVdbFile * @return the VdbElement * @throws Exception */ public static VdbElement getVdbManifest( final File dynamicVdbFile ) throws Exception { final VdbElement[] manifest = new VdbElement[1]; try { OperationUtil.perform(new Unreliable() { InputStream fileStream = null; @Override public void doIfFails() { } @Override public void finallyDo() throws Exception { if (fileStream != null) fileStream.close(); } @Override public void tryToDo() throws Exception { final Unmarshaller unmarshaller = getJaxbContext().createUnmarshaller(); unmarshaller.setSchema(getManifestSchema()); InputStream fileStream = new FileInputStream(dynamicVdbFile); manifest[0] = (VdbElement)unmarshaller.unmarshal(fileStream); fileStream.close(); } }); } catch (Exception ex) { if( ! (ex.getCause() instanceof UnmarshalException) ) { VdbPlugin.UTIL.log(IStatus.ERROR, ex, "Error finding VDB manifest for file: " + dynamicVdbFile.getName()); } return null; } return manifest[0]; } /** * @param xmlString * @return the VdbElement * @throws Exception */ public static VdbElement getVdbManifest( final String xmlString ) throws Exception { final VdbElement[] manifest = new VdbElement[1]; try { OperationUtil.perform(new Unreliable() { InputStream fileStream = null; @Override public void doIfFails() { } @Override public void finallyDo() throws Exception { if (fileStream != null) fileStream.close(); } @Override public void tryToDo() throws Exception { final Unmarshaller unmarshaller = getJaxbContext().createUnmarshaller(); unmarshaller.setSchema(getManifestSchema()); InputStream fileStream = new ByteArrayInputStream(xmlString.getBytes("UTF-8")); //$NON-NLS-1$ manifest[0] = (VdbElement)unmarshaller.unmarshal(fileStream); } }); } catch (Exception ex) { VdbPlugin.UTIL.log(ex); return null; } return manifest[0]; } /** * @param file * @return version the vdb version string may return null */ public static String getVdbVersion( final IFile file ) { try { if (file.exists()) { VdbElement manifest = VdbUtil.getVdbManifest(file); if (manifest != null) { return manifest.getVersion(); } } } catch (Exception ex) { VdbPlugin.UTIL.log(ex); } return "0"; } /** * Simple method that peeks inside a VDB manifest to check if the VDB was built with Teiid 8.0 or greater * The vdb version property value was added in Teiid Designer 8.2. So it's relatively safe to do this check. * * @param file * @return vdb is based on Teiid 7 */ public static boolean isVdbTeiidVersion8orGreater( final IFile file) { if (file == null || ! file.exists()) return false; ValidationVersionCallback callback = new ValidationVersionCallback(file); VdbFileProcessor processor = new VdbFileProcessor(callback); processor.process(); ITeiidServerVersion validationVersion = callback.getValidationVersion(); if (callback.hasException() || validationVersion == null) return false; return validationVersion.isGreaterThanOrEqualTo(Version.TEIID_8_0); } /** * @param modelElement the vdb model element * @return the uuid string. may be null */ public static String getUuid(final ModelElement modelElement) { for (final PropertyElement property : modelElement.getProperties()) { final String name = property.getName(); if (ModelElement.MODEL_UUID.equals(name)) { return property.getValue(); } } return null; } /** * Builds a comma-separated string from an array of strings * @param values an array of strings * @return string of comma separated values * */ public static String buildCommaDelimitedString(AllowedLanguages values) { StringBuilder sb = new StringBuilder(); int i=0; int numVal = values.size(); for (String val : values) { i++; sb.append(val); if( i< numVal ) { sb.append(StringConstants.COMMA).append(StringConstants.SPACE); } } return sb.toString(); } static JAXBContext getJaxbContext() throws JAXBException { return JAXBContext.newInstance(new Class<?>[] {VdbElement.class}); } /** * @return the vdb xsd as a resource */ public static URL getVdbXsd() { return VdbElement.class.getResource(FORWARD_SLASH + VDB_DEPLOYER_XSD); } /** * @return the vdb xsd as a resource stream */ public static InputStream getVdbXsdStream() { return VdbElement.class.getResourceAsStream(FORWARD_SLASH + VDB_DEPLOYER_XSD); } /** * @return the schema for the vdb manifest * * @throws SAXException */ public static Schema getManifestSchema() throws SAXException { final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); return schemaFactory.newSchema(getVdbXsd()); } /** * This method converts a vdb manifest model type and model path to a Designer ModelType object * Reason being that an XML Schema (TYPE) model is defined in the vdb manifest as "OTHER" * @param vdbModelType * @param modelPath * @return ModelType */ public static ModelType getModelType(String vdbModelType, String modelPath) { if (vdbModelType == OTHER && modelPath.toUpperCase().endsWith(".XSD")) { //$NON-NLS-1$ return ModelType.TYPE_LITERAL; } return ModelType.get(vdbModelType); } /** * Simple check to see if the model file is in the vdb * * @param theVdb * @param theModelFile * @return true if model exists by name in vdb * @throws Exception */ public static boolean modelInVdb(final IFile theVdb, final IFile theModelFile) throws Exception { if (theVdb.exists()) { VdbElement manifest = VdbUtil.getVdbManifest(theVdb); if (manifest != null) { for (ModelElement model : manifest.getModels()) { String modelName = model.getName()+ StringConstants.DOT_XMI; if (modelName.equalsIgnoreCase(theModelFile.getName())) { // We found the model, now replace the path return true; } } } } return false; } private static ITeiidServerVersion validateVdbVersion(final IFile theVdbFile, Collection<IStatus> statuses) { ITeiidServerVersion defaultTeiidVersion = ModelerCore.getTeiidServerVersion(); ITeiidServerVersion maxDesignerVersion = Version.TEIID_DEFAULT.get(); ValidationVersionCallback callback = new ValidationVersionCallback(theVdbFile); VdbFileProcessor processor = new VdbFileProcessor(callback); processor.process(); ITeiidServerVersion validationVersion = callback.getValidationVersion(); if (validationVersion == null) { /* No Validation version so probably pre-8.2 */ statuses.add( new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_noValidationVersionInVdb")) ); //$NON-NLS-1$ return null; } if (! validationVersion.compareTo(defaultTeiidVersion) && (validationVersion.isGreaterThan(defaultTeiidVersion) || validationVersion.isLessThan(defaultTeiidVersion))) /* Vdb version does not match teiid server version selected so may not deploy */ statuses.add( new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_differentValidationVersions", validationVersion, defaultTeiidVersion)) ); //$NON-NLS-1$ ITeiidServerVersion maxMmVersion = new TeiidServerVersion(maxDesignerVersion.getMajor(), maxDesignerVersion.getMinor(), ITeiidServerVersion.WILDCARD); if (validationVersion.isGreaterThan(maxMmVersion)) /* Vdb version is greater than the tested Designer Teiid Version which means all bets are off! */ statuses.add( new Status(IStatus.ERROR, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationError_validationVersionUnsupported")) ); //$NON-NLS-1$ return validationVersion; } /** * Simple check to see if the model file is in the vdb * * @param theVdbFile @return true if model exists by name in vdb */ public static MultiStatus validateVdbModelsInWorkspace(final IFile theVdbFile) { Collection<IStatus> statuses = new ArrayList<IStatus>(); IProject theProject = theVdbFile.getProject(); MultiStatus finalStatus = new MultiStatus(VdbConstants.PLUGIN_ID, 0, VdbPlugin.UTIL.getString("vdbValidationOK"), null); //$NON-NLS-1$ if (theVdbFile.exists()) { ITeiidServerVersion validationVersion = validateVdbVersion(theVdbFile, statuses); Vdb theVdb = null; VdbElement manifest = null; try { theVdb = new XmiVdb(theVdbFile); manifest = VdbUtil.getVdbManifest(theVdbFile); } catch (Exception ex) { statuses.add(new Status(IStatus.ERROR, VdbConstants.PLUGIN_ID, ex.getLocalizedMessage(), ex)); } if (theVdb != null && manifest != null) { // Prior to 9.0, VDB version had to be an integer value // for 9.0, the version was changed to a string value // so need to look for older runtime, then do an integer check and log a warning if it's a non-integer if(validationVersion != null && validationVersion.isLessThan(Version.TEIID_9_0)) { String vdbVersion = manifest.getVersion(); Integer intValue = Integer.getInteger(vdbVersion); if( intValue == null ) { statuses.add( new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_onlyIntegerVdbVersionsSupportedPriorToTeiid9")) ); //$NON-NLS-1$ } } // Check Security settings String securityDomain = theVdb.getSecurityDomain(); if( securityDomain != null ) { String gssPattern = theVdb.getGssPattern(); String passwordPattern = theVdb.getPasswordPattern(); String authenticationType = theVdb.getAuthenticationType(); if( authenticationType != null ) { if( gssPattern != null || passwordPattern != null ) { statuses.add( new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_singleAuthenticationType_0_willBeIgnored", authenticationType)) ); //$NON-NLS-1$ } } } for (ModelElement model : manifest.getModels()) { String modelName = model.getName(); // Check for models with ERRORS for( ProblemElement problem : model.getProblems() ) { if( problem.getSeverity() == Severity.ERROR ) { statuses.add( new Status(IStatus.ERROR, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationError_modelContainsErrors", modelName)) ); //$NON-NLS-1$ break; } } IResource resource = null; // Check if model with that name exists in project // first check if uuid == null String modelUuid = getUuid(model); if( modelUuid == null ) { Collection<IFile> resources = WorkspaceResourceFinderUtil.findIResourceInProjectByName(modelName, theProject); if( resources.size() == 1 ) { IFile someResource = resources.iterator().next(); if( ! ModelUtil.isVdbArchiveFile(someResource)) { resource = someResource; } } if( resource != null ) { statuses.add( new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_modelUuidMissing", modelName)) ); //$NON-NLS-1$ } } else { // Check if uuid exists in workspace or not resource = WorkspaceResourceFinderUtil.findIResourceByUUID(modelUuid); if( resource == null ) { // Find by name Collection<IFile> resources = WorkspaceResourceFinderUtil.findIResourceInProjectByName(modelName, theProject); Iterator<IFile> iterator = resources.iterator(); if (iterator.hasNext()) { IFile someResource = iterator.next(); if( ! ModelUtil.isVdbArchiveFile(someResource)) { resource = someResource; } } } } boolean nameChanged = false; if( resource == null ) { statuses.add( new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_noModelInWorkspace", modelName)) ); //$NON-NLS-1$ } else { // check same name // Check that resource is not a VDB String resourceName = FileUtils.getNameWithoutExtension(resource); if( ! modelName.equals(resourceName) ) { nameChanged = true; } else { if( ModelUtil.isVdbArchiveFile(resource) ) { continue; } } String path = model.getPath(); // Check IPath IPath iPath = new Path(path); IResource expectedResourceAtPath = ModelerCore.getWorkspace().getRoot().findMember(iPath); if( expectedResourceAtPath == null || nameChanged ) { statuses.add(new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_modelExistsWithDifferentLocationOrName", //$NON-NLS-1$ modelName, resource.getFullPath()))); } else { // Is it in sync if( ! isSynchronized(theVdb, (IFile)expectedResourceAtPath)) { statuses.add(new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_modelNotSynchronized", //$NON-NLS-1$ modelName))); } } } // Check for single source binding but mutliple sources if( model.getSources() != null && model.getSources().size() > 1 ) { boolean multiSourceIsFalse = true; for( PropertyElement prop : model.getProperties() ) { if( prop.getName().equals(ModelElement.SUPPORTS_MULTI_SOURCE) || prop.getName().equals(ModelElement.MULTI_SOURCE) ) { // Check boolean property if( Boolean.parseBoolean(prop.getValue()) ) { multiSourceIsFalse = false; break; } } } if( multiSourceIsFalse ) { statuses.add(new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_singleSourceModelHasMultipleSources", //$NON-NLS-1$ modelName))); } } // Check for Missing Translator type and JNDI name and add WARNINGs if( model.getSources() != null ) { for( SourceElement elem : model.getSources()) { if( StringUtilities.isEmpty(elem.getTranslatorName()) ) { statuses.add(new Status(IStatus.ERROR, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_sourceMissingTranslatorType", //$NON-NLS-1$ modelName, theVdb.getName()))); break; } } for( SourceElement elem : model.getSources()) { if( StringUtilities.isEmpty(elem.getJndiName()) ) { statuses.add(new Status(IStatus.WARNING, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationWarning_sourceMissingJndiName", //$NON-NLS-1$ modelName, theVdb.getName()))); break; } } } } // Check for duplicate model and/or user file names Map<String, String> fileFileNames = new HashMap<String, String>(); Set<String> modelNamesWithMultiple = new HashSet<String>(); for (ModelElement model : manifest.getModels()) { String modelName = model.getName(); if( fileFileNames.get(modelName.toUpperCase()) != null ) { modelNamesWithMultiple.add(modelName); } else { fileFileNames.put(modelName.toUpperCase(), modelName); } } // Add a problem for duplicate model names for( String modelName : modelNamesWithMultiple ) { statuses.add(new Status(IStatus.ERROR, VdbConstants.PLUGIN_ID, VdbPlugin.UTIL.getString("vdbValidationError_duplicateModelNames", //$NON-NLS-1$ modelName, theVdb.getName()))); break; } } } else { statuses.add(new Status(IStatus.ERROR, VdbConstants.PLUGIN_ID, "ERROR : VDB " + theVdbFile.getName() + " does not exist")); //$NON-NLS-1$ //$NON-NLS-2$ } if( ! statuses.isEmpty() ) { final IStatus[] result = new IStatus[statuses.size()]; statuses.toArray(result); finalStatus = new MultiStatus(VdbConstants.PLUGIN_ID, 0, result, "ERROR : VDB " + theVdbFile.getName() + " has problems", null); //$NON-NLS-1$ //$NON-NLS-2$ } return finalStatus; } /** * @param modelName * @param pathIncludingModel * @param vdb * @return if model already exists in VDB or not */ public static boolean modelAlreadyExistsInVdb(String modelName, IPath pathIncludingModel, Vdb vdb) { // Check for duplicate model and/or user file names Map<String, String> existingNames = new HashMap<String, String>(); Set<String> existingPaths = new HashSet<String>(); for (VdbEntry model : vdb.getModelEntries()) { String existingName = model.getPath().removeFileExtension().lastSegment(); IPath path = model.getPath(); existingNames.put(existingName.toUpperCase(), existingName); existingPaths.add(path.toString().toUpperCase()); } if(existingNames.get(modelName.toUpperCase()) != null && !existingPaths.contains(pathIncludingModel.toString().toUpperCase())) { return true; } return false; } /** * Method to determine whether or not a model file (xmi) can be added to a VDB. * The basic check is to look at the existing model names (without extension) and the target file to be added * and include any dependent models that would be added. ALL of these names (without extension) must be unique * and ignore case * * @param model file either .xmi * @param theVdb can be null * @return true if model can be added to an existing VDB * @throws Exception */ public static boolean canAddModelToVdb(final IFile model, final Vdb theVdb) throws Exception { ModelResource mr = ModelUtil.getModel(model); // Assume existing names will not include duplicates, but the MAP will insure they are unique Map<String, String> existingNames = new HashMap<String, String>(); Set<String> existingPaths = new HashSet<String>(); if( theVdb != null ) { for (VdbEntry modelEntry : theVdb.getModelEntries()) { String existingName = modelEntry.getPath().removeFileExtension().lastSegment(); IPath path = modelEntry.getPath(); existingNames.put(existingName.toUpperCase(), existingName); existingPaths.add(path.toString().toUpperCase()); } } // Check target model first String targetModelName = FileUtils.getNameWithoutExtension(model); if( existingNames.get(targetModelName.toUpperCase()) != null && !existingPaths.contains(model.getFullPath().toString().toUpperCase())) { return false; } existingNames.put(targetModelName.toUpperCase(), targetModelName); // Now get dependent models and check those ResourceFinder finder = null; finder = ModelerCore.getModelContainer().getResourceFinder(); Resource[] refs = finder.findReferencesFrom(mr.getEmfResource(), true, false); for( Resource res : refs ) { if (ModelUtil.isXsdFile(res)) { // See TEIIDDES-2120 // Xsd files are now added to 'Other Files' so are // not applicable to this test continue; } ModelResource modelRes = ModelUtil.getModel(res); String refModelName = modelRes.getCorrespondingResource().getFullPath().removeFileExtension().lastSegment(); IPath refModelPath = modelRes.getCorrespondingResource().getFullPath(); if( existingNames.get(refModelName.toUpperCase()) != null && !existingPaths.contains(refModelPath.toString().toUpperCase())) { return false; } existingNames.put(refModelName.toUpperCase(), refModelName); } return true; } /** * @param theVdb the vdb * @param theModelFile the model file in the workspace that is also in the VDB * @return if model file in vdb is synchronized */ public static boolean isSynchronized(final Vdb theVdb, final IFile theModelFile) { long fileCheckSum = 0L; boolean foundCheckSum = false; try { fileCheckSum = getCheckSum(theModelFile); foundCheckSum = true; } catch (Exception ex) { foundCheckSum = false; } if( foundCheckSum ) { for( VdbEntry modelEntry : theVdb.getModelEntries()) { if( modelEntry.getPath().lastSegment().equalsIgnoreCase(theModelFile.getName()) ) { return modelEntry.getChecksum() == fileCheckSum; } } } return false; } /** * Compute checksum for the given file. * * @param f The file for which checksum needs to be computed * @return The checksum * @throws Exception * @since 4.3 */ public static long getCheckSum(final IFile f) throws Exception { CoreArgCheck.isNotNull(f); InputStream is = null; try { is = f.getContents(); return ChecksumUtil.computeChecksum(is).getValue(); } finally { if (is != null) try { is.close(); } catch (final IOException err1) { } } } /** * Method which returns a list of models in your workspace that have the wrong path defined in the specified VDB * * @param theVdb the vdb * @return the list of models with wrong paths in VDB * @throws Exception */ public static Collection<IFile> getModelsWithWrongPaths(final IFile theVdb) throws Exception { Collection<IFile> misMatchedResources = new ArrayList<IFile>(); if (theVdb.exists()) { IProject theProject = theVdb.getProject(); VdbElement manifest = VdbUtil.getVdbManifest(theVdb); if (manifest != null) { for (ModelElement model : manifest.getModels()) { String modelName = model.getName()+ StringConstants.DOT_XMI; Collection<IFile> resources = WorkspaceResourceFinderUtil.findIResourceInProjectByName(modelName, theProject); if( resources.size() == 1 ) { String path = model.getPath(); IResource matchingResource = resources.iterator().next(); // Check IPath IPath iPath = new Path(path); IResource resource = ModelerCore.getWorkspace().getRoot().findMember(iPath); if( resource == null ) { misMatchedResources.add((IFile)matchingResource); } } } } } return misMatchedResources; } /** * @param modelElement * @return the collection of model import strings */ public static Collection<String> getModelImports(ModelElement modelElement) { Collection<String> imports = new ArrayList<String>(); for( PropertyElement element : modelElement.getProperties() ) { if( element.getName().equalsIgnoreCase(ModelElement.IMPORTS) ) { imports.add(element.getValue()); } } return imports; } private static VdbModelEntry getVdbModelEntry(ModelElement element, Vdb actualVDB) { for( VdbEntry modelEntry : actualVDB.getModelEntries()) { if( modelEntry.getPath().removeFileExtension().lastSegment().equalsIgnoreCase(element.getName()) ) { return (VdbModelEntry)modelEntry; } } return null; } /* * Simple method to return a project relative path to a project folder or sub-folder based on the model path * defined in another project (or same project) * * Method removes the "project" segment from the input path and appends the rest to the target project */ private static IPath getProjectRelativeModelPath(final String modelPathInVdb, final IProject targetProject) { IPath vdbModelPath = new Path(modelPathInVdb); IPath targetPath = new Path(StringConstants.EMPTY_STRING); int iSegs = vdbModelPath.segmentCount(); if( iSegs > 1 ) { for( int i=1; i<iSegs; i++ ) { targetPath = targetPath.append(vdbModelPath.segment(i)); } } return targetPath; } /** * * @param zipFile * @param zipEntryFullPath * @param projectRelativeTargetFolder * @return true if successful, false if not * @throws Exception */ private static boolean extractFileFromVdbToSameProject(final IFile zipFile, final String zipEntryFullPath, IPath projectRelativeTargetFolder) throws Exception { ZipInputStream zin = null; boolean result = false; try { String zipFilePath = ModelUtil.getLocation(zipFile).toOSString(); String projectFilePath = ModelUtil.getLocation(ModelerCore.getWorkspace().getRoot()) + zipFile.getProject().getFullPath().toOSString(); FileInputStream fin = new FileInputStream(zipFilePath); BufferedInputStream bin = new BufferedInputStream(fin); zin = new ZipInputStream(bin); ZipEntry ze = null; while ((ze = zin.getNextEntry()) != null) { String entryName = '/' + ze.getName(); if (entryName.equals(zipEntryFullPath)) { String finalModelPath = projectFilePath + '/' + projectRelativeTargetFolder; File entryFile = new File(finalModelPath); if( !entryFile.getParentFile().exists() ) { entryFile.getParentFile().mkdirs(); } FileOutputStream outstream = new FileOutputStream(finalModelPath); byte[] buffer = new byte[8192]; int len; while ((len = zin.read(buffer)) != -1) { outstream.write(buffer, 0, len); } outstream.close(); zin.closeEntry(); result = true; break; } } } finally { if( zin != null ) { try { zin.close(); } catch (IOException ex) { ex.printStackTrace(); } } } zipFile.getProject().refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); return result; } /** * @param theVdb the VDB * * @return status of synchronize * @throws Exception */ public static boolean synchronizeWorkspace(final IFile theVdb) throws Exception { boolean result = false; if (! theVdb.exists()) return result; IProject theProject = theVdb.getProject(); VdbElement manifest = VdbUtil.getVdbManifest(theVdb); if (manifest == null) return result; for (ModelElement model : manifest.getModels()) { String modelUuid = VdbUtil.getUuid(model); IResource resource = null; if (modelUuid != null) { resource = WorkspaceResourceFinderUtil.findIResourceByUUID(modelUuid); } else { // Find my model name Collection<IFile> resources = WorkspaceResourceFinderUtil.findIResourceInProjectByName(model.getName() + StringConstants.DOT_XMI, theProject); if (resources.size() == 1) { resource = resources.iterator().next(); } } // Check if resource is found or not. if (resource == null) { // Note that we have a View model that is in a VDB and it's source is removed (VDB.removeEntry()) // then that action will remove the View Model as well. // We need to prevent this OR need to tell the user we can't do anything about this and maybe // bail from this method. // Construct model path IPath targetPath = getProjectRelativeModelPath(model.getPath(), theVdb.getProject()); //extractModelFromVdb(theVdb, model, targetPath); extractFileFromVdbToSameProject(theVdb, model.getPath(), targetPath); result = true; } } for (EntryElement nextRes : manifest.getEntries()) { // Find my resource (schema? jar?) IPath path = new Path(nextRes.getPath()); IFile resource = WorkspaceResourceFinderUtil.findIResourceByPath(path); // Check if resource is found or not. if (resource == null) { // Note that we have a View model that is in a VDB and it's source is removed (VDB.removeEntry()) // then that action will remove the View Model as well. // We need to prevent this OR need to tell the user we can't do anything about this and maybe // bail from this method. // Construct model path IPath targetPath = getProjectRelativeModelPath(nextRes.getPath(), theVdb.getProject()); //extractModelFromVdb(theVdb, model, targetPath); extractFileFromVdbToSameProject(theVdb, nextRes.getPath(), targetPath); result = true; } } return result; } /** * @param theVdb the VDB * @param extractMissingModels * @param updateValidationVersion * * @throws Exception */ public static void synchronizeVdb(final IFile theVdb, boolean extractMissingModels, boolean updateValidationVersion) throws Exception { if (! theVdb.exists()) return; IProject theProject = theVdb.getProject(); VdbElement manifest = VdbUtil.getVdbManifest(theVdb); if (manifest == null) return; if (extractMissingModels) { synchronizeWorkspace(theVdb); } Vdb actualVDB = new XmiVdb(theVdb, true); Set<ModelElement> modelsToReplace = new HashSet<ModelElement>(); Collection<IResource> matchingResources = new ArrayList<IResource>(); Set<ModelElement> modelsNotInWorkspace = new HashSet<ModelElement>(); Map<String, IResource> oldModelPathToResourceMap = new HashMap<String, IResource>(); Set<String> dependentViewModelPaths = new HashSet<String>(); for (ModelElement model : manifest.getModels()) { Collection<String> modelImports = VdbUtil.getModelImports(model); for (String importedModelPath : modelImports) { for (ModelElement model_2 : manifest.getModels()) { if (model_2.getPath().equals(importedModelPath)) { dependentViewModelPaths.add(model.getPath()); break; } } } } for (ModelElement model : manifest.getModels()) { String modelName = model.getName(); String modelUuid = VdbUtil.getUuid(model); IResource resource = null; boolean addTheUuid = (modelUuid == null); boolean nameWasChanged = false; if (!addTheUuid) { resource = WorkspaceResourceFinderUtil.findIResourceByUUID(modelUuid); if (resource != null) { nameWasChanged = !resource.getFullPath().removeFileExtension().lastSegment().equalsIgnoreCase(modelName); } } else { // Find my model name IPath modelPath = new Path(model.getPath()); Collection<IFile> resources = WorkspaceResourceFinderUtil.findIResourceInProjectByName(model.getName() + CoreStringUtil.Constants.DOT + modelPath.getFileExtension(), theProject); if (resources.size() == 1) { resource = resources.iterator().next(); } } // Check if resource is found or not. if (resource != null) { String path = model.getPath(); // Check IPath IPath iPath = new Path(path); IResource expectedResourceAtPath = ModelerCore.getWorkspace().getRoot().findMember(iPath); boolean fixThePath = (expectedResourceAtPath == null); oldModelPathToResourceMap.put(model.getPath(), resource); if (fixThePath || nameWasChanged || addTheUuid) { for (VdbEntry modelEntry : actualVDB.getModelEntries()) { if (modelEntry.getPath().removeFileExtension().lastSegment().equalsIgnoreCase(modelName)) { modelsToReplace.add(model); matchingResources.add(resource); break; } } } } else { // Note that we have a View model that is in a VDB and it's source is removed (VDB.removeEntry()) // then that action will remove the View Model as well. // We need to prevent this OR need to tell the user we can't do anything about this and maybe // bail from this method. modelsNotInWorkspace.add(model); // Construct model path // IPath targetPath = getProjectRelativeModelPath(model.getPath(), theVdb.getProject()); // //extractModelFromVdb(theVdb, model, targetPath); // VdbUiUtil.extractFileFromVdbToSameProject(theVdb, model.getPath(), targetPath); } } // Check to see that any dependent View models are in the modelsNotInWorkspace list if (!modelsNotInWorkspace.isEmpty()) { // check if any are "dependent" (i.e. view models) for (ModelElement missingModel : modelsNotInWorkspace) { if (dependentViewModelPaths.contains(missingModel.getPath())) { // TODO: Throw up dialog saying can't synchronize because view model does not exist in workspace return; } } } // Loop through the changed models and remove/add them back as model entries Collection<String> modelPathsToReplace = new ArrayList<String>(); for (ModelElement element : modelsToReplace) { VdbModelEntry modelEntry = getVdbModelEntry(element, actualVDB); if (modelEntry != null) { modelPathsToReplace.add(element.getPath()); actualVDB.removeEntry(modelEntry); } } // Then loop through dependentViewModels list and add back any that are NOT in the VDB yet. These will be the // View model VdbModelEntry() instances that are auto-removed when "source" models are removed by VdbModelEntry.dispose() method. for (String thePath : modelPathsToReplace) { IResource matchingResource = oldModelPathToResourceMap.get(thePath); if (matchingResource != null) { actualVDB.addEntry(matchingResource.getFullPath()); } } for (String thePath : dependentViewModelPaths) { IResource matchingResource = oldModelPathToResourceMap.get(thePath); if (matchingResource != null) { actualVDB.addEntry(matchingResource.getFullPath()); } } actualVDB.synchronize(); if( updateValidationVersion ) { actualVDB.setValidationDateTime(new Date()); actualVDB.setValidationVersion(ModelerCore.getTeiidServerVersion().toString()); } actualVDB.save(); theVdb.refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor()); } /** * @param theVdb * @param targetName * @return the set of data role names for a VDB */ public static Set<String> getDataRoleNames(Vdb theVdb, String targetName) { Set<String> names = new HashSet<String>(); Collection<DataRole> entries = theVdb.getDataRoles(); for( DataRole role : entries) { names.add(role.getName()); } if( targetName != null && !targetName.isEmpty() ) { names.remove(targetName); } return names; } // /** // * Determines if a the provided VDB will successfully deploy based on the target server version and the // * VDB's version value // * // * @param serverVersion // * @param vdbFile // * @return true if invalid VDB version for target server. false if not // */ // public static boolean vdbHasInvalidVersionForServer(ITeiidServerVersion serverVersion, IFile vdbFile) { // VdbElement manifest = null; // // try { // // manifest = VdbUtil.getVdbManifest(vdbFile); // // } catch (Exception ex) { // ex.printStackTrace(); // return false; // } // // if( manifest != null ) { // String vdbVersion = manifest.getVersion(); // try { // Integer intValue = Integer.valueOf(vdbVersion); // return false; // } catch (NumberFormatException e) { // return true; // } // } // // return false; // } }