/* * Copyright 2014 Red Hat, Inc. and/or its affiliates. * * 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 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.guvnor.asset.management.client.editors.repository.wizard; import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.annotation.PostConstruct; import javax.enterprise.context.Dependent; import javax.enterprise.event.Event; import javax.inject.Inject; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Widget; import org.guvnor.asset.management.client.editors.repository.wizard.pages.RepositoryInfoPage; import org.guvnor.asset.management.client.editors.repository.wizard.pages.RepositoryStructurePage; import org.guvnor.asset.management.client.editors.repository.wizard.pages.RepositoryWizardPage; import org.guvnor.asset.management.client.i18n.Constants; import org.guvnor.asset.management.service.AssetManagementService; import org.guvnor.asset.management.service.RepositoryStructureService; import org.guvnor.common.services.project.client.repositories.ConflictingRepositoriesPopup; import org.guvnor.common.services.project.client.util.POMDefaultOptions; import org.guvnor.common.services.project.model.Build; import org.guvnor.common.services.project.model.GAV; import org.guvnor.common.services.project.model.MavenRepositoryMetadata; import org.guvnor.common.services.project.model.POM; import org.guvnor.common.services.project.service.DeploymentMode; import org.guvnor.common.services.project.service.GAVAlreadyExistsException; import org.guvnor.common.services.project.service.ProjectRepositoryResolver; import org.guvnor.structure.repositories.Repository; import org.guvnor.structure.repositories.RepositoryAlreadyExistsException; import org.guvnor.structure.repositories.RepositoryEnvironmentConfigurations; import org.guvnor.structure.repositories.RepositoryService; import org.guvnor.structure.security.RepositoryFeatures; import org.jboss.errai.bus.client.api.messaging.Message; import org.jboss.errai.common.client.api.Caller; import org.jboss.errai.common.client.api.ErrorCallback; import org.jboss.errai.common.client.api.RemoteCallback; import org.uberfire.backend.vfs.Path; import org.uberfire.client.callbacks.Callback; import org.uberfire.commons.data.Pair; import org.uberfire.ext.widgets.common.client.common.BusyPopup; import org.uberfire.ext.widgets.common.client.common.popups.errors.ErrorPopup; import org.uberfire.ext.widgets.core.client.resources.i18n.CoreConstants; import org.uberfire.ext.widgets.core.client.wizards.AbstractWizard; import org.uberfire.ext.widgets.core.client.wizards.WizardPage; import org.uberfire.mvp.Command; import org.uberfire.rpc.SessionInfo; import org.uberfire.security.authz.AuthorizationManager; import org.uberfire.workbench.events.NotificationEvent; @Dependent public class CreateRepositoryWizard extends AbstractWizard { private final List<WizardPage> pages = new ArrayList<WizardPage>(); private RepositoryInfoPage infoPage; private RepositoryStructurePage structurePage; private CreateRepositoryWizardModel model = new CreateRepositoryWizardModel(); private Caller<RepositoryService> repositoryService; private Caller<RepositoryStructureService> repositoryStructureService; private Caller<ProjectRepositoryResolver> repositoryResolverService; private Caller<AssetManagementService> assetManagementService; private Event<NotificationEvent> notification; private SessionInfo sessionInfo; private ConflictingRepositoriesPopup conflictingRepositoriesPopup; private POMDefaultOptions pomDefaultOptions; private AuthorizationManager authorizationManager; private Callback<Void> onCloseCallback = null; private boolean assetsManagementIsGranted = false; public CreateRepositoryWizard() { //Zero-parameter constructor for CDI proxies } @Inject public CreateRepositoryWizard( final RepositoryInfoPage infoPage, final RepositoryStructurePage structurePage, final CreateRepositoryWizardModel model, final Caller<RepositoryService> repositoryService, final Caller<RepositoryStructureService> repositoryStructureService, final Caller<ProjectRepositoryResolver> repositoryResolverService, final Caller<AssetManagementService> assetManagementService, final Event<NotificationEvent> notification, final SessionInfo sessionInfo, final ConflictingRepositoriesPopup conflictingRepositoriesPopup, final POMDefaultOptions pomDefaultOptions, final AuthorizationManager authorizationManager ) { this.infoPage = infoPage; this.structurePage = structurePage; this.model = model; this.repositoryService = repositoryService; this.repositoryStructureService = repositoryStructureService; this.repositoryResolverService = repositoryResolverService; this.assetManagementService = assetManagementService; this.notification = notification; this.sessionInfo = sessionInfo; this.conflictingRepositoriesPopup = conflictingRepositoriesPopup; this.pomDefaultOptions = pomDefaultOptions; this.authorizationManager = authorizationManager; } @PostConstruct public void setupPages() { pages.add( infoPage ); infoPage.initialise(); structurePage.initialise(); infoPage.setModel( model ); structurePage.setModel( model ); infoPage.setHandler( new RepositoryInfoPage.RepositoryInfoPageHandler() { @Override public void managedRepositoryStatusChanged( boolean status ) { managedRepositorySelected( status ); } } ); setAssetsManagementGrant(); } @Override public List<WizardPage> getPages() { return pages; } @Override public Widget getPageWidget( int pageNumber ) { final RepositoryWizardPage page = (RepositoryWizardPage) this.pages.get( pageNumber ); final Widget w = page.asWidget(); return w; } @Override public String getTitle() { return Constants.INSTANCE.NewRepository(); } @Override public int getPreferredHeight() { return 600; } @Override public int getPreferredWidth() { return 700; } @Override public void isComplete( final Callback<Boolean> callback ) { final int[] unCompletedPages = { this.pages.size() }; final boolean[] completed = { false }; //only when all pages are complete we can say the wizard is complete. for ( WizardPage page : this.pages ) { page.isComplete( new Callback<Boolean>() { @Override public void callback( final Boolean result ) { if ( Boolean.TRUE.equals( result ) ) { unCompletedPages[ 0 ]--; if ( unCompletedPages[ 0 ] == 0 ) { completed[ 0 ] = true; } } } } ); } callback.callback( completed[ 0 ] ); } @Override public void complete() { doComplete(); } @Override public void close() { super.close(); invokeOnCloseCallback(); } public void onCloseCallback( final Callback<Void> callback ) { this.onCloseCallback = callback; } private void managedRepositorySelected( final boolean selected ) { if ( assetsManagementIsGranted ) { boolean updateDefaultValues = false; if ( selected && !pages.contains( structurePage ) ) { pages.add( structurePage ); updateDefaultValues = true; } else { pages.remove( structurePage ); } super.start(); if ( updateDefaultValues ) { setStructureDefaultValues(); } } } public void pageSelected( final int pageNumber ) { super.pageSelected( pageNumber ); if ( pageNumber == 1 ) { infoPage.setStructurePageWasVisited( true ); structurePage.setStructurePageWasVisited( true ); setStructureDefaultValues(); } } private void doComplete() { repositoryService.call( new RemoteCallback<String>() { @Override public void callback( final String normalizedName ) { if ( !model.getRepositoryName().equals( normalizedName ) ) { if ( !Window.confirm( CoreConstants.INSTANCE.RepositoryNameInvalid() + " \"" + normalizedName + "\". " + CoreConstants.INSTANCE.DoYouAgree() ) ) { return; } String unNormalizedName = model.getRepositoryName(); model.setRepositoryName( normalizedName ); if ( unNormalizedName.equals( model.getProjectName() ) ) { model.setProjectName( normalizedName ); } if ( unNormalizedName.equals( model.getArtifactId() ) ) { model.setArtifactId( normalizedName ); } } if ( model.isManged() ) { showBusyIndicator( Constants.INSTANCE.ValidatingProjectGAV() ); final GAV gav = new GAV( model.getGroupId(), model.getArtifactId(), model.getVersion() ); repositoryResolverService.call( new RemoteCallback<Set<MavenRepositoryMetadata>>() { @Override public void callback( final Set<MavenRepositoryMetadata> metadatas ) { if ( metadatas.isEmpty() ) { doRepositoryCreation( DeploymentMode.VALIDATED ); } else { hideBusyIndicator(); conflictingRepositoriesPopup.setContent( gav, metadatas, new Command() { @Override public void execute() { conflictingRepositoriesPopup.hide(); doRepositoryCreation( DeploymentMode.FORCED ); } } ); conflictingRepositoriesPopup.show(); } } } ).getRepositoriesResolvingArtifact( gav ); } else { doRepositoryCreation( DeploymentMode.VALIDATED ); } } } ).normalizeRepositoryName( model.getRepositoryName() ); } private void parentComplete() { super.complete(); } private void doRepositoryCreation( final DeploymentMode mode ) { final String scheme = "git"; final String alias = model.getRepositoryName().trim(); final RepositoryEnvironmentConfigurations configuration = new RepositoryEnvironmentConfigurations(); configuration.setManaged( assetsManagementIsGranted && model.isManged() ); parentComplete(); showBusyIndicator( Constants.INSTANCE.CreatingRepository() ); repositoryService.call( new RemoteCallback<Repository>() { @Override public void callback( final Repository repository ) { hideBusyIndicator(); notification.fire( new NotificationEvent( Constants.INSTANCE.RepoCreationSuccess() ) ); getRepositoryCreatedSuccessCallback( mode ).callback( repository ); } }, new ErrorCallback<Message>() { @Override public boolean error( final Message message, final Throwable throwable ) { try { hideBusyIndicator(); throw throwable; } catch ( RepositoryAlreadyExistsException ex ) { showErrorPopup( CoreConstants.INSTANCE.RepoAlreadyExists() ); } catch ( Throwable ex ) { showErrorPopup( CoreConstants.INSTANCE.RepoCreationFail() + " \n" + throwable.getMessage() ); } invokeOnCloseCallback(); return true; } } ).createRepository( model.getOrganizationalUnit(), scheme, alias, configuration ); } private RemoteCallback<Repository> getRepositoryCreatedSuccessCallback( final DeploymentMode mode ) { return new RemoteCallback<Repository>() { @Override public void callback( final Repository repository ) { if ( model.isManged() ) { POM pom = new POM(); pom.setName( model.getProjectName() ); pom.setDescription( model.getProjectDescription() ); pom.getGav().setGroupId( model.getGroupId() ); pom.getGav().setArtifactId( model.getArtifactId() ); pom.getGav().setVersion( model.getVersion() ); if ( !model.isMultiModule() ) { pom.setPackaging( pomDefaultOptions.getPackaging() ); pom.setBuild( new Build() ); pom.getBuild().setPlugins( pomDefaultOptions.getBuildPlugins() ); } final String url = GWT.getModuleBaseURL(); final String baseUrl = url.replace( GWT.getModuleName() + "/", "" ); doRepositoryInitialization( pom, repository, baseUrl, mode ); } else { invokeOnCloseCallback(); } } }; } private void doRepositoryInitialization( final POM pom, final Repository repository, final String baseUrl, final DeploymentMode mode ) { showBusyIndicator( Constants.INSTANCE.InitializingRepository() ); repositoryStructureService.call( new RemoteCallback<Path>() { @Override public void callback( Path path ) { hideBusyIndicator(); notification.fire( new NotificationEvent( Constants.INSTANCE.RepoInitializationSuccess() ) ); getRepositoryInitializedSuccessCallback().callback( new Pair<Repository, Path>( repository, path ) ); } }, new ErrorCallback<Message>() { @Override public boolean error( final Message message, final Throwable throwable ) { //We check for clashing GAVs before the Repository is created. Therefore this *should* never really //fail; but there's a window of opportunity if User A already has a Project for GAV1 and Install/Deploys //it in between User B creating a new Repository for GAV1 and the Project structure being initialised. hideBusyIndicator(); // The *real* Throwable is wrapped in an InvocationTargetException when ran as a Unit Test and invoked with Reflection. final Throwable _throwable = ( throwable.getCause() == null ? throwable : throwable.getCause() ); if ( _throwable instanceof GAVAlreadyExistsException ) { final GAVAlreadyExistsException gae = (GAVAlreadyExistsException) _throwable; conflictingRepositoriesPopup.setContent( gae.getGAV(), gae.getRepositories(), new Command() { @Override public void execute() { conflictingRepositoriesPopup.hide(); doRepositoryInitialization( pom, repository, baseUrl, DeploymentMode.FORCED ); } } ); conflictingRepositoriesPopup.show(); } else { showErrorPopup( Constants.INSTANCE.RepoInitializationFail() + " \n" + _throwable.getMessage() ); invokeOnCloseCallback(); } return true; } } ).initRepositoryStructure( pom, baseUrl, repository, model.isMultiModule(), mode ); } private RemoteCallback<Pair<Repository, Path>> getRepositoryInitializedSuccessCallback() { return new RemoteCallback<Pair<Repository, Path>>() { @Override public void callback( Pair<Repository, Path> pair ) { if ( model.isConfigureRepository() ) { assetManagementService.call( new RemoteCallback<Void>() { @Override public void callback( final Void o ) { notification.fire( new NotificationEvent( Constants.INSTANCE.RepoConfigurationStarted() ) ); invokeOnCloseCallback(); } }, new ErrorCallback<Message>() { @Override public boolean error( final Message message, final Throwable throwable ) { showErrorPopup( Constants.INSTANCE.RepoConfigurationStartFailed() + " \n" + throwable.getMessage() ); invokeOnCloseCallback(); return true; } } ).configureRepository( pair.getK1().getAlias(), "master", "dev", "release", normalizeVersionNumber( model.getVersion() ) ); } else { invokeOnCloseCallback(); } } }; } private String normalizeVersionNumber( String version ) { version = version != null ? version.trim() : null; if ( version != null && version.contains( "-SNAPSHOT" ) ) { return version.replace( "-SNAPSHOT", "" ); } else { return version; } } private void invokeOnCloseCallback() { if ( onCloseCallback != null ) { onCloseCallback.callback( null ); } } private void showBusyIndicator( final String message ) { BusyPopup.showMessage( message ); } private void hideBusyIndicator() { BusyPopup.close(); } private void showErrorPopup( final String message ) { ErrorPopup.showMessage( message ); } private void setStructureDefaultValues() { if ( model.getRepositoryName() != null ) { structurePage.setProjectName( model.getRepositoryName() ); structurePage.setArtifactId( model.getRepositoryName() ); } if ( model.getOrganizationalUnit() != null ) { structurePage.setGroupId( model.getOrganizationalUnit().getDefaultGroupId() ); } structurePage.setProjectDescription( null ); structurePage.setVersion( "1.0.0-SNAPSHOT" ); structurePage.fireEvent(); } private void setAssetsManagementGrant() { assetsManagementIsGranted = authorizationManager.authorize( RepositoryFeatures.CONFIGURE_REPOSITORY, sessionInfo.getIdentity() ); infoPage.enableManagedRepoCreation( assetsManagementIsGranted ); } }