/******************************************************************************* * 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.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.ebayopensource.turmeric.eclipse.buildsystem.resources.SOAConstants; import org.ebayopensource.turmeric.eclipse.buildsystem.resources.SOAMessages; import org.ebayopensource.turmeric.eclipse.codegen.model.ConsumerCodeGenModel; import org.ebayopensource.turmeric.eclipse.codegen.utils.CodegenInvoker; import org.ebayopensource.turmeric.eclipse.core.logging.SOALogger; import org.ebayopensource.turmeric.eclipse.core.resources.constants.SOAProjectConstants; import org.ebayopensource.turmeric.eclipse.exception.resources.SOAActionExecutionFailedException; import org.ebayopensource.turmeric.eclipse.exception.validation.ValidationInterruptedException; import org.ebayopensource.turmeric.eclipse.registry.ExtensionPointFactory; import org.ebayopensource.turmeric.eclipse.registry.intf.IArtifactValidator; import org.ebayopensource.turmeric.eclipse.registry.intf.IArtifactValidator2; import org.ebayopensource.turmeric.eclipse.registry.intf.IValidationStatus; import org.ebayopensource.turmeric.eclipse.repositorysystem.core.GlobalRepositorySystem; import org.ebayopensource.turmeric.eclipse.repositorysystem.core.ISOARootLocator; import org.ebayopensource.turmeric.eclipse.repositorysystem.model.BaseCodeGenModel; import org.ebayopensource.turmeric.eclipse.resources.model.AssetInfo; import org.ebayopensource.turmeric.eclipse.resources.model.IAssetInfo; import org.ebayopensource.turmeric.eclipse.resources.util.MarkerUtil; import org.ebayopensource.turmeric.eclipse.resources.util.SOAConsumerUtil; import org.ebayopensource.turmeric.eclipse.utils.collections.ListUtil; import org.ebayopensource.turmeric.eclipse.utils.core.VersionUtil; import org.ebayopensource.turmeric.eclipse.utils.lang.StringUtil; import org.ebayopensource.turmeric.eclipse.utils.plugin.EclipseMessageUtils; import org.ebayopensource.turmeric.eclipse.utils.plugin.ProgressUtil; import org.ebayopensource.turmeric.eclipse.utils.plugin.WorkspaceUtil; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.wst.wsdl.validation.internal.IValidationMessage; /** * All Action calls comes here * * @author smathew */ public class ActionUtil { private static final SOALogger logger = SOALogger.getLogger(); /** The Constant WST_FACET_NATURE_ID. */ public static final String WST_FACET_NATURE_ID = "org.eclipse.wst.common.project.facet.core.nature"; /** The Constant WST_MODULECORE_NATURE_ID. */ public static final String WST_MODULECORE_NATURE_ID = "org.eclipse.wst.common.modulecore.ModuleCoreNature"; private static final String WSI_PREFIX = "WSI"; private static final String AS_PREFIX = "AS"; /** * Validates the selected object before executing an action. Most of the * context menu actions use the adaptable interface to find the project * associated with the selection and this API checks if the selected object * is adaptable. If it is not adaptable it returns null rather throwing an * exception, because there are also other ways to find the project. if its * adaptable this API will adapt the selected object to a project and * returns it back * * @param selectedObject the selected object * @param logger the logger * @return the i project */ public static IProject preValidateAction(final Object selectedObject, SOALogger logger) { if (!(selectedObject instanceof IAdaptable)) { logger.warning(StringUtil.formatString(SOAMessages.NOT_ADAPTABLE, selectedObject)); return null; } final IProject project = (IProject) ((IAdaptable) selectedObject) .getAdapter(IProject.class); if (project == null || !project.isAccessible()) { logger.warning(StringUtil.formatString(SOAMessages.INVALIDPROJECT, project.getName())); return null; } return project; } /** * Generates the type mappings file for a given project. Gentype used is * TypeMappings. Mainly called from actions, because we have grouped, impl, * intf gentypes and users might still want to regenerate their type * mappings just in case. * * @param project the project * @param monitor the monitor * @return true, if successful * @throws Exception the exception */ public static boolean generateTypeMappings(IProject project, IProgressMonitor monitor) throws Exception { CodegenInvoker codegenInvoker = CodegenInvoker.init(project); ProgressUtil.progressOneStep(monitor); BaseCodeGenModel codeGenModel = BuilderUtil.buildBaseCodeGenModel( project, monitor); ProgressUtil.progressOneStep(monitor); // Gen Type Mappings codeGenModel = ModelTransformer.transformToGenTypeTypeMappings( codeGenModel, project); ProgressUtil.progressOneStep(monitor); codegenInvoker.execute(codeGenModel); ProgressUtil.progressOneStep(monitor); return true; } /** * Generates the Global Service Config File. This file used to get generated * automatically before and now we have made it an on demand file. Reason is * obvious, Not all projects are supposed to have this file. And for the * same reason we have now given a context menu to generate it on demand. * The gen-type used is GlobalServerConfig. * * @param project the project * @param monitor the monitor * @return true, if successful * @throws Exception the exception */ public static boolean generateGlobalServiceConfig(IProject project, IProgressMonitor monitor) throws Exception { CodegenInvoker codegenInvoker = CodegenInvoker.init(project); ProgressUtil.progressOneStep(monitor); BaseCodeGenModel codeGenModel = BuilderUtil.buildBaseCodeGenModel( project, monitor); ProgressUtil.progressOneStep(monitor); codeGenModel = ModelTransformer.transformToGenTypeGlobalServerConfig( codeGenModel, project); ProgressUtil.progressOneStep(monitor); codegenInvoker.execute(codeGenModel); ProgressUtil.progressOneStep(monitor); return true; } /** * Generates the Global Client Config File. This file used to get generated * automatically before and now we have made it an on demand file. Reason is * obvious, Not all projects are supposed to have this file. And for the * same reason we have now given a context menu to generate it on demand. * The gen-type used is GlobalClientConfig. * * @param project the project * @param monitor the monitor * @return true, if successful * @throws Exception the exception */ public static boolean generateGlobalClientConfig(IProject project, IProgressMonitor monitor) throws Exception { CodegenInvoker codegenInvoker = CodegenInvoker.init(project); ProgressUtil.progressOneStep(monitor); BaseCodeGenModel codeGenModel = BuilderUtil.buildBaseCodeGenModel( project, monitor); ProgressUtil.progressOneStep(monitor); if (codeGenModel instanceof ConsumerCodeGenModel) { // This call is required here to set the required services and other // fetch steps GenType Consumer codeGenModel = ModelTransformer.transformToGenTypeConsumer( (ConsumerCodeGenModel) codeGenModel, project); ProgressUtil.progressOneStep(monitor); codeGenModel = ModelTransformer .transformToGenTypeGlobalClientConfigConsumer( (ConsumerCodeGenModel) codeGenModel, project); ProgressUtil.progressOneStep(monitor); } else { codeGenModel = ModelTransformer .transformToGenTypeGlobalClientConfig(codeGenModel, project); ProgressUtil.progressOneStep(monitor); } codegenInvoker.execute(codeGenModel); ProgressUtil.progressOneStep(monitor); return true; } /** * Generates the Skeleton for the given implementation project. Skeleton * means everything except implementation class. Again everything means all * generated files :). * * @param project the project * @param monitor the monitor * @return true, if successful * @throws Exception the exception */ public static boolean generateServiceImplSkeleton(IProject project, IProgressMonitor monitor) throws Exception { return generateServiceImplSkeleton(project, false, monitor); } /** * Generates the Skeleton for the given implementation project. Additionally * if the overwriteImplClass is true it will overwrite the implementation * java class as well. The reason why we have a separate variable to govern * it, is because in most cases people will have domain logic in the * implementation class and we do not want to overwrite it withour a * confirmation from the user. * * @param project the project * @param overwriteImplClass the overwrite impl class * @param monitor the monitor * @return true, if successful * @throws Exception the exception */ public static boolean generateServiceImplSkeleton(IProject project, boolean overwriteImplClass, IProgressMonitor monitor) throws Exception { CodegenInvoker codegenInvoker = CodegenInvoker.init(project); ProgressUtil.progressOneStep(monitor); BaseCodeGenModel codeGenModel = BuilderUtil.buildBaseCodeGenModel( project, monitor); ProgressUtil.progressOneStep(monitor); if (overwriteImplClass) { codeGenModel = ModelTransformer .transformToGenTypeSISkeltonOverwriteImplClass( codeGenModel, project); } else { codeGenModel = ModelTransformer.transformToGenTypeSISkelton( codeGenModel, project); } ProgressUtil.progressOneStep(monitor); codegenInvoker.execute(codeGenModel); ProgressUtil.progressOneStep(monitor); return true; } /** * Generates the web xml. This is web application descriptor. * * @param project the project * @param templates the templates * @param templateLoadingClass the template loading class * @param monitor the monitor * @return true, if successful * @throws Exception the exception */ public static boolean generateWebXml(IProject project, final Map<String, String> templates, final Class<?> templateLoadingClass, IProgressMonitor monitor) throws Exception { CodegenInvoker codegenInvoker = CodegenInvoker.init(project); ProgressUtil.progressOneStep(monitor); BaseCodeGenModel codeGenModel = BuilderUtil.buildBaseCodeGenModel( project, monitor); ProgressUtil.progressOneStep(monitor); codeGenModel = ModelTransformer.transformToGenTypeWebXml(codeGenModel, project); ProgressUtil.progressOneStep(monitor); codegenInvoker.execute(codeGenModel); ProgressUtil.progressOneStep(monitor); if (templates != null && templateLoadingClass != null) { final Map<String, Object> data = new ConcurrentHashMap<String, Object>(); data.put(SOAConstants.SVC_NAME, codeGenModel.getServiceName()); BuildSystemCodeGen .generateArtifactsFromTemplates( templateLoadingClass, templates, data, project .getFolder(SOAProjectConstants.FOLDER_GEN_WEB_CONTENT), monitor); } return true; } /** * Validates the given project to make sure that it can consume a service. * we have to make sure that the source directory is not the the root * directory, it does not have more than one source directory, * * @param project the project * @return the i status */ public static IStatus validateJavaProjectForConvertingToConsumer( final IProject project) { final ISOARootLocator locator = GlobalRepositorySystem.instanceOf() .getActiveRepositorySystem().getSOARootLocator(); if (locator.getRoot().isPrefixOf(project.getLocation()) == false) { return EclipseMessageUtils.createErrorStatus(StringUtil .formatString(SOAMessages.ERR_LOC_ROOT_INCORRECT, locator .getRoot())); } IFile projectFile = project.getFile(SOAProjectConstants.FILE_PROJECT); if (projectFile.exists() == true && projectFile.isReadOnly() == true) { return EclipseMessageUtils.createErrorStatus(StringUtil .formatString(SOAMessages.JAVA_CLASSPATH_READONLY, projectFile.getLocationURI().toString())); } IFile classpathFile = project .getFile(SOAProjectConstants.FILE_CLASSPATH); if (classpathFile.exists() == true && classpathFile.isReadOnly() == true) { return EclipseMessageUtils.createErrorStatus(StringUtil .formatString(SOAMessages.JAVA_CLASSPATH_READONLY, classpathFile.getLocationURI().toString())); } return Status.OK_STATUS; } private static IStatus validateUsingAS(IFile wsdlWorkspaceFile, URL wsdlFile, List<IStatus> statuses, boolean needDowngrade, IProgressMonitor monitor) throws Exception { final List<IArtifactValidator> validators = ExtensionPointFactory .getArtifactValidators(); final List<IArtifactValidator> wsdlValidators = new ArrayList<IArtifactValidator>(); final String upperWSDL = SOAProjectConstants.WSDL .toUpperCase(Locale.US); for (IArtifactValidator validator : validators) { if (validator instanceof IArtifactValidator2) { if (((IArtifactValidator2) validator) .isAssertionServiceEnabled() == false) { continue; } } if (validator.getAllSupportedValidators().contains(upperWSDL) == true) { wsdlValidators.add(validator); } } if (wsdlValidators.isEmpty() == true) { // IStatus status = EclipseMessageUtils // .createErrorStatus(SOAMessages.WARNING_MISSING_ARTIFACT_VALIDATOR); // statuses.add(status); logger.warning(SOAMessages.WARNING_MISSING_ARTIFACT_VALIDATOR); return Status.CANCEL_STATUS; } Collections.sort(wsdlValidators, new Comparator<IArtifactValidator>() { @Override public int compare(IArtifactValidator v1, IArtifactValidator v2) { if (v1 instanceof IArtifactValidator2 && v2 instanceof IArtifactValidator2) { return VersionUtil.compare(((IArtifactValidator2) v1) .getVersion(), ((IArtifactValidator2) v2) .getVersion()); } else if (v1 instanceof IArtifactValidator2) { return -1; } else if (v2 instanceof IArtifactValidator2) { return 1; } return 0; } }); ProgressUtil.progressOneStep(monitor); // sorted in ascending order, so the last one is the most matched one. IArtifactValidator validator = wsdlValidators.get(0); logger.info("Validating WSDL with assertion service ->", wsdlFile .toString() + " using validator->" + validator.getClass().getName()); InputStream is = null; try { is = wsdlFile.openStream(); byte[] contents = IOUtils.toByteArray(is); logger.info("Validating WSDL with assertion service->", wsdlFile .toString()); IStatus status = validator.validateArtifact(contents, upperWSDL, monitor); ProgressUtil.progressOneStep(monitor); logger.info("Validating WSDL result->", status); if (status.isOK() == false) { if (status.isMultiStatus() == true) { // Add markers to wsdl file for (IStatus wsdlValidationStatus : status.getChildren()) { int lineNumber = -1; if (wsdlValidationStatus instanceof IValidationStatus) { lineNumber = ((IValidationStatus) wsdlValidationStatus) .getLineNumber(); } // if need downgrade, only care about error status and // change it to warning. if (needDowngrade == true) { if (wsdlValidationStatus.getSeverity() == IStatus.ERROR) { wsdlValidationStatus = EclipseMessageUtils .createStatus(wsdlValidationStatus .getMessage(), IStatus.WARNING); } else { continue; } } if (wsdlWorkspaceFile != null) { MarkerUtil .createWSDLMarker(wsdlWorkspaceFile, AS_PREFIX, wsdlValidationStatus, lineNumber); logger.warning(AS_PREFIX + ":" + wsdlValidationStatus.getMessage(), wsdlValidationStatus.getException()); } statuses.add(wsdlValidationStatus); } } else { IStatus statusDump = status; if (needDowngrade == true) { if (statusDump.getSeverity() == IStatus.ERROR) { statusDump = EclipseMessageUtils.createStatus( statusDump.getMessage(), IStatus.WARNING); } else { statusDump = null; } } if (statusDump != null) { // Add markers to wsdl file if (wsdlWorkspaceFile != null) { MarkerUtil.createWSDLMarker(wsdlWorkspaceFile, AS_PREFIX, statusDump, -1); logger.warning(AS_PREFIX + ":" + statusDump.getMessage(), statusDump .getException()); } statuses.add(statusDump); } } } else { logger.info("Validating WSDL with AS validator finsihed." + " No AS violations found."); } return status; } finally { IOUtils.closeQuietly(is); ProgressUtil.progressOneStep(monitor); } } /** * Validate using wtp. * * @param wsdlWorkspaceFile the wsdl workspace file * @param wsdlFile the wsdl file * @param statuses the statuses * @param needDowngrade the need downgrade * @param monitor the monitor * @throws ValidationInterruptedException the validation interrupted exception * @throws CoreException the core exception * @throws MalformedURLException the malformed url exception * @throws IOException Signals that an I/O exception has occurred. */ @SuppressWarnings("restriction") public static void validateUsingWTP(IFile wsdlWorkspaceFile, URL wsdlFile, List<IStatus> statuses, boolean needDowngrade, IProgressMonitor monitor) throws ValidationInterruptedException, CoreException, MalformedURLException, IOException { // TODO This WSDL validation is different with the one used in in // codeGen. logger .info( "Validating WSDL with WTP validator->", wsdlFile.toExternalForm() + " using validator->" + org.eclipse.wst.wsdl.validation.internal.eclipse.WSDLValidator .getInstance().getClass().getName()); IValidationMessage[] validationMessages = org.eclipse.wst.wsdl.validation.internal.eclipse.WSDLValidator .getInstance().validate(wsdlFile.toExternalForm()) .getValidationMessages(); if (validationMessages != null && validationMessages.length > 0) { for (IValidationMessage validationMessage : validationMessages) { IStatus status = null; if (needDowngrade == true) { // downgrade error to warning, ignore warning. if (validationMessage.getSeverity() == IValidationMessage.SEV_ERROR) { status = EclipseMessageUtils .createStatus(validationMessage.getMessage(), IStatus.WARNING); } else { continue; } } else { if (validationMessage.getSeverity() == IValidationMessage.SEV_ERROR) { status = EclipseMessageUtils.createStatus( validationMessage.getMessage(), IStatus.ERROR); } else if (validationMessage.getSeverity() == IValidationMessage.SEV_WARNING) { status = EclipseMessageUtils .createStatus(validationMessage.getMessage(), IStatus.WARNING); } else { continue; } } statuses.add(status); if (wsdlWorkspaceFile != null) { MarkerUtil.createWSDLMarker(wsdlWorkspaceFile, WSI_PREFIX, status, validationMessage.getLine()); logger.warning(WSI_PREFIX + ":" + status.getMessage(), status.getException()); } } } else { logger.info("Validating WSDL with WTP validator finsihed." + " No WTP violations found."); } } /** * Validate service wsdl. * * @param wsdlFile the wsdl file * @param wsdlFileURL the wsdl file url * @param needASValidation the need as validation * @param needDowngrade the need downgrade * @param monitor the monitor * @return the i status * @throws Exception the exception */ public static IStatus validateServiceWSDL(IFile wsdlFile, URL wsdlFileURL, boolean needASValidation, boolean needDowngrade, IProgressMonitor monitor) throws Exception { final List<IStatus> statuses = new ArrayList<IStatus>(); // Clean all markers before validation if (wsdlFile != null) { MarkerUtil.cleanWSDLMarkers(wsdlFile); } validateUsingWTP(wsdlFile, wsdlFileURL, statuses, needDowngrade, monitor); if (needASValidation == true) { try { validateUsingAS(wsdlFile, wsdlFileURL, statuses, needDowngrade, monitor); } catch (Exception e) { logger.error(e); IStatus status = EclipseMessageUtils.createStatus( "Exception occures while Running Assertion Service Validation: " + e.getMessage(), IStatus.WARNING); statuses.add(status); // UIUtil.showErrorDialog( // SOAMessages.ERROR_SERVICE_RS_SERVICE_FAILED_TITLE, // SOAMessages.ERROR_SERVICE_RS_SERVICE_FAILED, e); } } else { logger.info("AS validation skipped."); } if (statuses.isEmpty() == false) { logger.warning(SOAMessages.ERROR_SERVICE_WSDL_VALIDATION_FAILED); return EclipseMessageUtils.createErrorMultiStatus(statuses, SOAMessages.ERROR_SERVICE_WSDL_VALIDATION_FAILED); } return Status.OK_STATUS; } /** * Clean project. * * @param project the project * @param monitor the monitor * @return the i status * @throws CoreException the core exception */ public static IStatus cleanProject(IProject project, IProgressMonitor monitor) throws CoreException { try { final Collection<IFolder> resources = new HashSet<IFolder>(); resources.add(project .getFolder(SOAProjectConstants.FOLDER_GEN_META_SRC)); resources.add(project .getFolder(SOAProjectConstants.FOLDER_GEN_TEST)); IFolder genClient = project .getFolder(SOAProjectConstants.FOLDER_GEN_SRC_CLIENT); IFolder genService = project .getFolder(SOAProjectConstants.FOLDER_GEN_SRC_SERVICE); if (genClient.isAccessible() == false && genService.isAccessible() == false) { resources.add(project .getFolder(SOAProjectConstants.FOLDER_GEN_SRC)); } resources.add(genClient); resources.add(genService); resources.add(project .getFolder(SOAProjectConstants.FOLDER_GEN_WEB_CONTENT)); logger.info("Start to clean project " + project.getName() + "..."); for (final IResource resource : resources) { if (resource.isAccessible()) { try { logger.info("Cleaning directory " + resource.getLocation() + "..."); FileUtils.cleanDirectory(resource.getLocation() .toFile()); ProgressUtil.progressOneStep(monitor); } catch (Exception e) { logger.error(e); throw new SOAActionExecutionFailedException(e); } } } logger.info("Clean project " + project.getName() + " finished."); project.refreshLocal(IResource.DEPTH_INFINITE, monitor); project.build(IncrementalProjectBuilder.CLEAN_BUILD, monitor); ProgressUtil.progressOneStep(monitor); } finally { monitor.done(); WorkspaceUtil.refresh(monitor, project); } return Status.OK_STATUS; } /** * generate config for zero config project * @param consumerProject * @param monitor * @return * @throws Exception */ public static IStatus generateConfigs(IProject consumerProject, IProgressMonitor monitor) throws Exception { final List<AssetInfo> services = new ArrayList<AssetInfo>(); for (AssetInfo asset : GlobalRepositorySystem.instanceOf().getActiveRepositorySystem() .getAssetRegistry().getDependencies(consumerProject.getName())) { if (IAssetInfo.TYPE_SERVICE_LIBRARY.equals(asset.getType())) { services.add(asset); } } logger.info("Generating configs for consumer project-> ", consumerProject.getName(), " with services ->", services ); final String clientName = SOAConsumerUtil.getClientName(consumerProject); BuildSystemCodeGen .generateArtifactsForAddedService( consumerProject, clientName, ListUtil .arrayList(SOAProjectConstants.DEFAULT_CLIENT_CONFIG_ENVIRONMENT), services, monitor); return Status.OK_STATUS; } /** * Wrapper to execute the job and schedule and show a message in the UI if * required * */ public static interface ActionJob { /** * Gets the target platform * * @return */ public String getTargetPlatform(); /** * Schedules the job for execution */ public void schedule(); /** * Setting the sub job for the parent job * * @param job */ public void setSubJob(Job job); /** * show message * @param title * @param message */ public void showMessage(String title, String message); } }