package org.ebayopensource.turmeric.eclipse.repositorysystem.utils; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; import org.ebayopensource.turmeric.eclipse.core.logging.SOALogger; import org.ebayopensource.turmeric.eclipse.exception.resources.SOAResourceModifyFailedException; import org.ebayopensource.turmeric.eclipse.registry.ExtensionPointFactory; import org.ebayopensource.turmeric.eclipse.registry.exception.ProviderException; import org.ebayopensource.turmeric.eclipse.registry.intf.IRegistryProvider; import org.ebayopensource.turmeric.eclipse.registry.models.SubmitAssetModel; import org.ebayopensource.turmeric.eclipse.repositorysystem.core.GlobalRepositorySystem; import org.ebayopensource.turmeric.eclipse.repositorysystem.core.ISOAProjectConfigurer; import org.ebayopensource.turmeric.eclipse.repositorysystem.core.TrackingEvent; import org.ebayopensource.turmeric.eclipse.repositorysystem.resources.SOAMessages; import org.ebayopensource.turmeric.eclipse.resources.model.ISOAProject; import org.ebayopensource.turmeric.eclipse.resources.model.SOAIntfMetadata; import org.ebayopensource.turmeric.eclipse.resources.model.SOAIntfProject; import org.ebayopensource.turmeric.eclipse.resources.util.SOAIntfUtil; import org.ebayopensource.turmeric.eclipse.resources.util.SOAServiceUtil; import org.ebayopensource.turmeric.eclipse.utils.core.VersionUtil; 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.ebayopensource.turmeric.eclipse.utils.ui.UIUtil; import org.ebayopensource.turmeric.eclipse.validator.utils.ValidateUtil; import org.ebayopensource.turmeric.eclipse.validator.utils.common.AbstractBaseAccessValidator; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.progress.UIJob; import org.osgi.framework.Version; /** * action util class * */ public class ActionUtil { private static final SOALogger logger = SOALogger.getLogger(); /** * get assert model of the project. * * @param project * @return * @throws Exception */ public static SubmitAssetModel getAssetModel(IProject project) throws Exception { if (project == null || TurmericServiceUtils.isSOAInterfaceProject(project) == false) { throw new IllegalArgumentException( "Not a valid SOA interface project ->" + project); } // we trust the service name should be same as the project name final String serviceName = project.getName(); // construct the model final SubmitAssetModel model = new SubmitAssetModel(); String natureId = GlobalRepositorySystem.instanceOf() .getActiveRepositorySystem() .getTurmericProjectNatureId(project); ISOAProject soaProject = SOAServiceUtil.loadSOAProject(project, natureId); SOAIntfMetadata metadata = (SOAIntfMetadata) soaProject.getMetadata(); if (StringUtils.isNotBlank(metadata.getPublicServiceName())) { // post 2.4 services model.setServiceName(metadata.getPublicServiceName()); model.setAdminName(metadata.getServiceName()); } else { // pre 2.4 services, adminName=serviceName model.setServiceName(metadata.getServiceName()); model.setAdminName(metadata.getServiceName()); } model.setNamespacePart(metadata.getServiceNamespacePart()); model.setInterfaceProjectPath(project.getLocation().toString()); model.setServiceLayer(metadata.getServiceLayer()); model.setServiceNamespace(metadata.getTargetNamespace()); model.setServiceVersion(metadata.getServiceVersion()); model.setServiceWsdlLocation(SOAServiceUtil .getWsdlFile(project, serviceName).getLocation().toString()); if (StringUtils.isBlank(metadata.getServiceDomainName())) { logger.warning( "Service domain name is missing. Please check service_intf_project.properties of the project and make sure 'domainName' propery is set.", "\n\nIf the service is created before installing AR plugin, then please either re-create the service or manually add 'domainName={DomainName}' to service_intf_project.properties and substitute the {DomainName}."); } model.setServiceDomain(metadata.getServiceDomainName()); return model; } /** * when exception thrown from AR, it is not friendly. This method gets the * root cause of the exception and create an error status for the exception, * using a description * SOAMessages.ERROR_FAIL_TO_SUBMIT_SERVICE_VERSION_TO_AR * * @param e * @return */ private static IStatus handleExceptionFromAR(Throwable e) { logger.error(e); Throwable rootCause = e.getCause(); if (rootCause == null) { return EclipseMessageUtils.createErrorStatus(e); } else { while (rootCause.getCause() != null) { rootCause = rootCause.getCause(); } if (rootCause instanceof LinkageError) { logger.error(rootCause); return EclipseMessageUtils.createErrorStatus( SOAMessages.ERROR_AR_OUT_OF_DATE, null); } else { return EclipseMessageUtils.createErrorStatus( SOAMessages.ERROR_FAIL_TO_SUBMIT_SERVICE_VERSION_TO_AR, rootCause); } } } /** * Submite a service maintenance version. For Backward Compatibility, using * reflect to call new added method. Show error message if method not found * and notify users to update to latest version. * * @param project * @return * @throws Exception */ public static IStatus submitServiceMaintenanceVersion(IProject project, String newVersion) throws Exception { final IRegistryProvider regProvider = ExtensionPointFactory .getSOARegistryProvider(); if (regProvider == null) { return EclipseMessageUtils.createStatus( SOAMessages.WARNING_AR_NOT_AVAILABLE, IStatus.WARNING); } // use reflect for backward compatible. try { Method submitNewMaintenanceVersionMethod = regProvider.getClass() .getMethod("submitNewMaintenanceVersion", SubmitAssetModel.class); SubmitAssetModel model = getAssetModel(project); if (newVersion != null && newVersion.trim().length() > 0) { model.setServiceVersion(newVersion); } Object retStatus = submitNewMaintenanceVersionMethod.invoke( regProvider, model); return (IStatus) retStatus; } catch (NoSuchMethodException ex) { logger.error(ex); return EclipseMessageUtils.createErrorStatus( SOAMessages.ERROR_AR_OUT_OF_DATE, null); } catch (SecurityException ex) { return EclipseMessageUtils.createErrorStatus(ex); } catch (InvocationTargetException ex) { return handleExceptionFromAR(ex); } } /** * this is used to update interface project version. It combines the * following operations: 1) Update local metadata 2) sync version with AR if * AR is available. Notify users before doing it. It is cancelable. 3) do a * v3 build if it is v3 mode. Notify users before doing it. Library Catalog * version need to be synchronized before doing a v3 build. It is * cancelable. * * This method will be used in service property page when changing service * version. And also be used in V3 when calling a Build Service in right * click menu. * * This method is meant to be invoked in the UI thread. * * @param soaIntfProject * the interface project that need to be updated * @param oldVersion * service old version * @param newVersion * service new version. * @param silence * just for the repos that need build after version change. * @param monitor * the progress monitor */ public static void updateInterfaceProjectVersion( final SOAIntfProject soaIntfProject, String oldVersion, String newVersion, boolean silence, IProgressMonitor monitor) throws Exception { ISOAProjectConfigurer configurer = GlobalRepositorySystem.instanceOf() .getActiveRepositorySystem().getProjectConfigurer(); ProgressUtil.progressOneStep(monitor); if (StringUtils.equals(oldVersion, newVersion) == true) { logger.info("Service version not change : " + newVersion); // still need try to build service if version not changed. if (silence == true) { configurer.postServiceVersionUpdated(soaIntfProject, oldVersion, newVersion, silence, monitor); } return; } final IProject intfProject = soaIntfProject.getProject(); final String serviceName = intfProject.getName(); final IStatus status = new AbstractBaseAccessValidator() { @Override public List<IResource> getReadableFiles() { // should check the following files try { return GlobalProjectHealthChecker .getSOAProjectReadableResources(intfProject); } catch (Exception e) { logger.warning(e); } return new ArrayList<IResource>(1); } @Override public List<IResource> getWritableFiles() { final List<IResource> files = new ArrayList<IResource>(); files.add(SOAIntfUtil.getMetadataFile(intfProject, serviceName)); files.add(SOAServiceUtil.getWsdlFile(intfProject, serviceName)); return files; } }.validate(serviceName); final String messages = ValidateUtil .getFormattedStatusMessagesForAction(status); if (messages != null) { UIUtil.showErrorDialog(UIUtil.getActiveShell(), "Error", messages, (Throwable) null); return; } soaIntfProject.getMetadata().setServiceVersion(newVersion); // update local meta data. ProgressUtil.progressOneStep(monitor); configurer.updateProject(soaIntfProject, false, monitor); ProgressUtil.progressOneStep(monitor); // synchronize with AR if available. // although submitVersionToAssitionRepository will check // version change or not. here we add a check to avoid // creating a UIJob. if (GlobalRepositorySystem.instanceOf().getActiveRepositorySystem() .getActiveOrganizationProvider() .supportAssetRepositoryIntegration() == true && ExtensionPointFactory.getSOARegistryProvider() != null) { final Version newVer = new Version(newVersion); final Version oldVer = new Version(oldVersion); ProgressUtil.progressOneStep(monitor); // no version check here because it is checked when // version changed. UIJob updateVersion = new UIJob( "Synchornizing service version with Asset Repository.") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { try { ActionUtil.submitVersionToAssetRepository(newVer, oldVer, soaIntfProject.getProjectName(), monitor); } catch (Exception e) { logger.error(e); } return Status.OK_STATUS; } }; updateVersion.schedule(); } ProgressUtil.progressOneStep(monitor); // trigger build is needed. configurer.postServiceVersionUpdated(soaIntfProject, oldVersion, newVersion, silence, monitor); } /** * submit a new minor version to AR. * * @param project * @param newVersion * @return */ public static IStatus submitNewVersionAssetToSOARegistry(IProject project, String newVersion) { IRegistryProvider regProvider; try { regProvider = ExtensionPointFactory.getSOARegistryProvider(); if (regProvider == null) { return EclipseMessageUtils.createStatus( SOAMessages.WARNING_AR_NOT_AVAILABLE, IStatus.WARNING); } final SubmitAssetModel model = getAssetModel(project); if (newVersion != null && newVersion.trim().length() > 0) { model.setServiceVersion(newVersion); } // ProgressUtil.progressOneStep(monitor); // submit the model return regProvider.submitNewVersionForGovernance(model); } catch (CoreException e) { logger.error(e); return EclipseMessageUtils.createErrorStatus(e); } catch (ProviderException e) { return handleExceptionFromAR(e); } catch (Exception e) { logger.error(e); return EclipseMessageUtils.createErrorStatus(e); } } /** * submit version to assert repo * * @param newVersion * @param oldVersion * @param svcIntfName * @param monitor * @return * @throws Exception */ public static IStatus submitVersionToAssetRepository(Object newVersion, Object oldVersion, String svcIntfName, IProgressMonitor monitor) throws Exception { Version newVer = VersionUtil.getVersion(newVersion); Version oldVer = VersionUtil.getVersion(oldVersion); monitor.setTaskName("Synchronize service version with " + "Asset Repository for project->" + svcIntfName + ", from version [" + oldVer + "], to version [" + newVer + "]."); // no version format check here because it is checked when version // changed. // here we use not equal but not bigger than to decide if a version is // changed. // Only show when service version is changed this time. boolean miniorChanged = newVer.getMinor() != oldVer.getMinor(); boolean mantanChanged = newVer.getMicro() != oldVer.getMicro(); final boolean syncVersion; if (miniorChanged || mantanChanged) { syncVersion = UIUtil .openChoiceDialog( "Synchronize Service Version", "Service version has been updated to [" + newVer + "] in local metadata. " + "Would you like to synchronize service version change to the Asset Repository?", MessageDialog.QUESTION_WITH_CANCEL); } else { logger.info("Service Version not changed:" + newVer + ". Exist submitVersionToAssetRepository"); return Status.CANCEL_STATUS; } if (syncVersion == false) { return Status.CANCEL_STATUS; } try { IStatus result = null; IProject intfProj = WorkspaceUtil.getProject(svcIntfName); if (miniorChanged == true) { result = ActionUtil.submitNewVersionAssetToSOARegistry( intfProj, newVer.toString()); } else if (mantanChanged == true) { result = ActionUtil.submitServiceMaintenanceVersion(intfProj, newVer.toString()); } else { logger.info("Service Version not changed:" + newVer + ". Exist submitVersionToAssitionRepository"); } if (result != null) { if (result.isOK() == true) { GlobalRepositorySystem .instanceOf() .getActiveRepositorySystem() .trackingUsage( new TrackingEvent(ActionUtil.class .getName(), TrackingEvent.TRACKING_ACTION)); } else { UIUtil.showErrorDialog( (Shell) null, "Service Version Synchronize Failed", "Failed to synchronize service version with Asset Repository.", result); } } } catch (Exception e) { logger.error(e); throw new SOAResourceModifyFailedException( "Failed to synchronize service version for project->" + svcIntfName + ", from version [" + oldVer + "] to version [" + newVer + "].", e); } finally { monitor.done(); } return null; } }