/******************************************************************************* * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 *******************************************************************************/ package org.ebayopensource.turmeric.eclipse.buildsystem.utils; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.wsdl.Definition; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.ebayopensource.turmeric.eclipse.buildsystem.resources.SOAMessages; import org.ebayopensource.turmeric.eclipse.core.logging.SOALogger; import org.ebayopensource.turmeric.eclipse.core.resources.constants.SOAProjectConstants; import org.ebayopensource.turmeric.eclipse.exception.core.SOANullParameterException; import org.ebayopensource.turmeric.eclipse.exception.resources.SOAResourceNotAccessibleException; import org.ebayopensource.turmeric.eclipse.repositorysystem.model.BaseCodeGenModel; import org.ebayopensource.turmeric.eclipse.repositorysystem.utils.TurmericServiceUtils; import org.ebayopensource.turmeric.eclipse.resources.model.ISOAConsumerProject.SOAClientConfig; import org.ebayopensource.turmeric.eclipse.resources.model.SOAImplMetadata; import org.ebayopensource.turmeric.eclipse.resources.util.SOAConsumerUtil; import org.ebayopensource.turmeric.eclipse.resources.util.SOAImplUtil; import org.ebayopensource.turmeric.eclipse.resources.util.SOAServiceUtil; import org.ebayopensource.turmeric.eclipse.utils.lang.StringUtil; import org.ebayopensource.turmeric.eclipse.utils.plugin.JDTUtil; import org.ebayopensource.turmeric.eclipse.utils.plugin.ProgressUtil; import org.ebayopensource.turmeric.eclipse.utils.plugin.WorkspaceUtil; import org.ebayopensource.turmeric.eclipse.utils.wsdl.WSDLUtil; import org.eclipse.core.resources.ICommand; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProjectDescription; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; /** * The standard Utility class for the builder. Most builders will need the code * generation models and engine in their functions. This utility mainly caters * these two objects so that the builder has a commonality in the way in which * they invoke the code generation engine. * * @author smathew * */ public class BuilderUtil { /** * Foundation Code generation Model for builders. This is the base code * generation model which will be specialized before used in most cases. * Fills the metadata file(properties file) information into this model. All * the common data that any model required will be filled in at this phase. * But The gen type that is the most crucial factor is not decided here. It * is decided in the specialization phase. Its also called heavily from * context menu actions which includes codegen. * * @param project the project * @param monitor the monitor * @return the base code gen model * @throws Exception the exception */ public static BaseCodeGenModel buildBaseCodeGenModel(final IProject project, final IProgressMonitor monitor) throws Exception { return ModelTransformer.generateCodeGenModel(project, monitor); } /** * Returns trues if the WSDL file is changed. This is not a straight forward * API. It does not care about the changes in the WSDL file if it is not an * interface projects. So please don't use it as a generic API. * * @param delta the delta * @param project the project * @return true, if is wSDL file changed * @throws CoreException the core exception */ public static boolean isWSDLFileChanged(IResourceDelta delta, IProject project) throws CoreException { if (TurmericServiceUtils.isSOAInterfaceProject(project) == false) return false; final IFile wsdlFile = SOAServiceUtil.getWsdlFile(project, project .getName()); return isWSDLFileChanged(delta, wsdlFile.getFullPath()); } /** * Parses the delta and returns true if the WSDL file is changed. Recursive * function but does not use the visitor pattern. * * @param delta * @param wsdlPath * @return */ private static boolean isWSDLFileChanged(IResourceDelta delta, IPath wsdlPath) { if (delta == null) return false; for (IResourceDelta childDelta : delta .getAffectedChildren(IResourceDelta.CHANGED)) { if (childDelta.getFullPath().equals(wsdlPath)) return true; else if (isWSDLFileChanged(childDelta, wsdlPath)) { return true; } } return false; } /** * Returns true if the WSDL target name space has been changed in an * interface project. To be precise, it does NOT compare the WSDl's old name * space and the new name space, but compares the WSDL's current namespace * to the namespace in the implementation projects metadata file. * * @param delta the delta * @param serviceName the service name * @param intfProject the intf project * @param implProject the impl project * @return null means no change or the new targetNamespace otherwise. * @throws Exception the exception */ public static String isWSDLTargetNamespaceChanged(IResourceDelta delta, String serviceName, IProject intfProject, IProject implProject) throws Exception { final IFile wsdlFile = SOAServiceUtil.getWsdlFile(intfProject, serviceName); if (isWSDLFileChanged(delta, wsdlFile.getFullPath()) == false) return null; final Definition wsdl = WSDLUtil.readWSDL(wsdlFile.getLocation() .toString()); final SOAImplMetadata metadata = SOAImplUtil.loadServiceConfig( implProject, serviceName); if (!StringUtils.equals(wsdl.getTargetNamespace(), metadata .getTargetNamespace())) { return wsdl.getTargetNamespace(); } return null; } /** * Returns true if the WSDL target name space has been changed in an * interface project. To be precise, it does NOT compare the WSDl's old name * space and the new name space, but compares the WSDL's current namespace * to the namespace in the client config file of the consumer. * * @param serviceName the service name * @param intfProject the intf project * @param consumerProject the consumer project * @return null means no change or the new targetNamespace otherwise. * @throws Exception the exception */ public static String isWSDLTargetNamespaceChangedInConsumerProject( String serviceName, IProject intfProject, IProject consumerProject) throws Exception { final IFile wsdlFile = SOAServiceUtil.getWsdlFile(intfProject, serviceName); if (wsdlFile.isAccessible() == false) return null; final Definition wsdl = WSDLUtil.readWSDL(wsdlFile.getLocation() .toString()); String envName = null; if (SOAConsumerUtil.isOldClientConfigDirStructure(consumerProject) == false) { List<String> envs = SOAConsumerUtil.getClientEnvironmentList(consumerProject, null); if (envs.isEmpty() == false) envName = envs.get(0); } final SOAClientConfig clientConfig = SOAConsumerUtil.loadClientConfig( consumerProject, envName, serviceName); if (clientConfig == null) return null; if (!StringUtils.equals(wsdl.getTargetNamespace(), clientConfig .getTargetNamespace())) { return wsdl.getTargetNamespace(); } return null; } /** * This algorithm is the decision maker for all SOA builders. If this is * true builder gets kicked off and return silently otherwise. Remember * there are two builders most of the time and its very imp to silently * return at some point of time to avoid cyclic builds. * * @param delta the delta * @param project the project * @param criteriaString - * this is mostly the extensions that we are interested in * rebuilding. something like WSDT EXT, XSD EXT etc * @return true, if successful * @throws Exception the exception */ public static boolean shouldBuild(IResourceDelta delta, IProject project, String... criteriaString) throws Exception { // first time the delta is null if (delta == null) return true; BuildResourceDeltaVisitor buildDeltaVisitor = new BuildResourceDeltaVisitor( project, criteriaString); delta.accept(buildDeltaVisitor); return buildDeltaVisitor.isBuildRequired(); } /** * This is required because V3 appears to mess with the build order. This * will stay here until V3 comes up with a bug fix. * * @param project the project * @throws CoreException the core exception */ public static void reOrderBuildersIfRequired(IProject project) throws CoreException { ICommand commands[] = project.getDescription().getBuildSpec(); List<ICommand> oldList = Arrays.asList(commands); List<ICommand> newList = new ArrayList<ICommand>(); for (ICommand command : commands) { newList.add(command); } // Scanning if the order has changed for (int i = 0; i < newList.size(); i++) { // true means order changed if (!StringUtils.equals(oldList.get(i).getBuilderName(), newList .get(i).getBuilderName())) { IProjectDescription description = project.getDescription(); description.setBuildSpec(newList.toArray(new ICommand[0])); project.setDescription(description, IResource.FORCE, ProgressUtil.getDefaultMonitor(null)); } } } /** * Cleans the bin/META-INF folder and do a refresh. * * @param project the project * @param monitor the monitor * @throws IOException Signals that an I/O exception has occurred. * @throws CoreException the core exception */ public static void cleanBinMetaInfDir(final IProject project, IProgressMonitor monitor) throws IOException, CoreException { final IFolder metaFolder = project .getFolder(SOAProjectConstants.FOLDER_OUTPUT_DIR + WorkspaceUtil.PATH_SEPERATOR + SOAProjectConstants.FOLDER_META_INF); metaFolder.getParent().refreshLocal(IResource.DEPTH_ONE, monitor); if (metaFolder.isAccessible()) { SOALogger.getLogger().warning( StringUtil.formatString(SOAMessages.CLEAN_FOLDER, metaFolder .getLocation())); FileUtils.cleanDirectory(metaFolder.getLocation().toFile()); } } /** * Returns the required project for any given project. Scans the build bath * and finds all the project references and returns it back. * * @param project the project * @param natureIds the nature ids * @return the required projects */ public static IProject[] getRequiredProjects(IProject project, String... natureIds) { IJavaProject javaProject = JavaCore.create(project); if (javaProject == null) return new IProject[0]; ArrayList<IProject> projects = new ArrayList<IProject>(); try { for (final IClasspathEntry entry : javaProject .getResolvedClasspath(true)) { if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { IProject reqProject = WorkspaceUtil.getProject(entry .getPath()); if (reqProject != null && !projects.contains(reqProject)) { if (natureIds != null) { for (String natureId : natureIds) { if (reqProject.hasNature(natureId)) { projects.add(reqProject); break; } } } else { projects.add(reqProject); } } } } } catch (Exception e) { return new IProject[0]; } IProject[] result = new IProject[projects.size()]; projects.toArray(result); return result; } /** * Creates missing source folders as per the projects java model. If it * exists, it is a no operation. * * @param project the project * @param monitor the monitor * @throws CoreException the core exception */ public static void generateSourceDirectories(final IProject project, final IProgressMonitor monitor) throws CoreException { if (project == null || project.isAccessible() == false) return; final IPath projectPath = project.getFullPath(); final List<String> missingFolders = new ArrayList<String>(); for (IPath srcPath : JDTUtil.getSourceDirectories(project)) { if (projectPath.isPrefixOf(srcPath)) { srcPath = srcPath.removeFirstSegments(1); } final IFolder srcFolder = project.getFolder(srcPath); if (!srcFolder.exists()) { srcFolder.refreshLocal(IResource.DEPTH_ZERO, monitor); if (srcFolder.exists() == false) { missingFolders.add(srcPath.toString()); } } } if (missingFolders.isEmpty() == false) { WorkspaceUtil.createFolders(project, missingFolders, monitor); } } /** * Deletes the gen folder content of interface projects(for now). The types * are generated in the folder and we don't want stale java types to live * there. Washing it out in this call. * * @param project the project * @throws CoreException the core exception * @throws SOANullParameterException the sOA null parameter exception * @throws SOAResourceNotAccessibleException the sOA resource not accessible exception */ public static void cleanGenFolders(IProject project) throws CoreException, SOANullParameterException, SOAResourceNotAccessibleException { // Intf Project if (TurmericServiceUtils.isSOAInterfaceProject(project)) { IFolder clientFolder = project .getFolder(SOAProjectConstants.FOLDER_GEN_SRC_CLIENT); WorkspaceUtil.deleteContents(clientFolder, true); } } }