/*
* Copyright 2017 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.kie.workbench.common.screens.projecteditor.client.build;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Maps;
import org.guvnor.common.services.project.builder.model.BuildResults;
import org.guvnor.common.services.project.builder.service.BuildService;
import org.guvnor.common.services.project.client.repositories.ConflictingRepositoriesPopup;
import org.guvnor.common.services.project.context.ProjectContext;
import org.guvnor.common.services.project.model.GAV;
import org.guvnor.common.services.project.service.DeploymentMode;
import org.guvnor.common.services.project.service.GAVAlreadyExistsException;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.common.client.api.Caller;
import org.jboss.errai.common.client.api.RemoteCallback;
import org.kie.server.api.model.KieContainerStatus;
import org.kie.server.api.model.ReleaseId;
import org.kie.server.controller.api.model.spec.ContainerSpec;
import org.kie.server.controller.api.model.spec.ServerTemplate;
import org.kie.server.controller.api.model.spec.ServerTemplateKey;
import org.kie.workbench.common.screens.projecteditor.client.editor.DeploymentScreenPopupViewImpl;
import org.kie.workbench.common.screens.projecteditor.client.resources.ProjectEditorResources;
import org.kie.workbench.common.screens.server.management.service.SpecManagementService;
import org.kie.workbench.common.widgets.client.callbacks.CommandWithThrowableDrivenErrorCallback;
import org.uberfire.ext.widgets.common.client.common.HasBusyIndicator;
import org.uberfire.mvp.Command;
import org.uberfire.workbench.events.NotificationEvent;
public class BuildExecutor {
public interface View extends HasBusyIndicator {
void showABuildIsAlreadyRunning();
}
private DeploymentScreenPopupViewImpl deploymentScreenPopupView;
private Caller<SpecManagementService> specManagementService;
private Caller<BuildService> buildServiceCaller;
private Event<BuildResults> buildResultsEvent;
private Event<NotificationEvent> notificationEvent;
private ConflictingRepositoriesPopup conflictingRepositoriesPopup;
private ProjectContext projectContext;
private View view;
private boolean building = false;
@Inject
public BuildExecutor(DeploymentScreenPopupViewImpl deploymentScreenPopupView,
Caller<SpecManagementService> specManagementService,
Caller<BuildService> buildServiceCaller,
Event<BuildResults> buildResultsEvent,
Event<NotificationEvent> notificationEvent,
ConflictingRepositoriesPopup conflictingRepositoriesPopup,
ProjectContext projectContext) {
this.deploymentScreenPopupView = deploymentScreenPopupView;
this.specManagementService = specManagementService;
this.buildServiceCaller = buildServiceCaller;
this.buildResultsEvent = buildResultsEvent;
this.notificationEvent = notificationEvent;
this.conflictingRepositoriesPopup = conflictingRepositoriesPopup;
this.projectContext = projectContext;
}
public void init(final View view) {
this.view = view;
}
private Map<Class<? extends Throwable>, CommandWithThrowableDrivenErrorCallback.CommandWithThrowable> onBuildAndDeployGavExistsHandler = new HashMap<Class<? extends Throwable>, CommandWithThrowableDrivenErrorCallback.CommandWithThrowable>() {{
put(GAVAlreadyExistsException.class,
new CommandWithThrowableDrivenErrorCallback.CommandWithThrowable() {
@Override
public void execute(final Throwable parameter) {
view.hideBusyIndicator();
conflictingRepositoriesPopup.setContent(projectContext.getActiveProject().getPom().getGav(),
((GAVAlreadyExistsException) parameter).getRepositories(),
() -> {
conflictingRepositoriesPopup.hide();
getBuildDeployCommand(DeploymentMode.FORCED).execute();
});
conflictingRepositoriesPopup.show();
}
});
}};
public void triggerBuild() {
getSafeExecutedCommand(getBuildCommand()).execute();
}
public void triggerBuildAndDeploy() {
specManagementService.call(new RemoteCallback<Collection<ServerTemplate>>() {
@Override
public void callback(final Collection<ServerTemplate> serverTemplates) {
final String defaultContainerId = projectContext.getActiveProject().getPom().getGav().getArtifactId() + "_" + projectContext.getActiveProject().getPom().getGav().getVersion();
final String defaultContainerAlias = projectContext.getActiveProject().getPom().getGav().getArtifactId();
final boolean defaultStartContainer = true;
if (serverTemplates.isEmpty()) {
getSafeExecutedCommand(getBuildDeployCommand(DeploymentMode.VALIDATED)).execute();
} else if (serverTemplates.size() == 1) {
buildDeployWithOneServerTemplateAvailable(serverTemplates,
defaultContainerId,
defaultContainerAlias,
defaultStartContainer);
} else {
buildDeployWithMultipleServerTemplatesAvailable(serverTemplates,
defaultContainerId,
defaultContainerAlias,
defaultStartContainer);
}
}
}).listServerTemplates();
}
private void buildDeployWithOneServerTemplateAvailable(Collection<ServerTemplate> serverTemplates,
String defaultContainerId,
String defaultContainerAlias,
boolean defaultStartContainer) {
final ServerTemplate serverTemplate = serverTemplates.iterator().next();
final Set<String> existingContainers = FluentIterable.from(serverTemplate.getContainersSpec()).transform(s -> s.getId()).toSet();
if (existingContainers.contains(defaultContainerId) == false) {
getSafeExecutedCommand(getBuildDeployProvisionCommand(DeploymentMode.VALIDATED,
defaultContainerId,
defaultContainerAlias,
serverTemplate.getId(),
defaultStartContainer)).execute();
} else {
deploymentScreenPopupView.setValidateExistingContainerCallback(containerName -> existingContainers.contains(containerName));
deploymentScreenPopupView.setContainerId(defaultContainerId);
deploymentScreenPopupView.setContainerAlias(defaultContainerAlias);
deploymentScreenPopupView.setStartContainer(defaultStartContainer);
deploymentScreenPopupView.configure(() -> {
final String containerId = deploymentScreenPopupView.getContainerId();
final String containerAlias = deploymentScreenPopupView.getContainerAlias();
final boolean startContainer = deploymentScreenPopupView.getStartContainer();
getSafeExecutedCommand(getBuildDeployProvisionCommand(DeploymentMode.VALIDATED,
containerId,
containerAlias,
serverTemplate.getId(),
startContainer)).execute();
deploymentScreenPopupView.hide();
});
deploymentScreenPopupView.show();
}
}
private void buildDeployWithMultipleServerTemplatesAvailable(Collection<ServerTemplate> serverTemplates,
String defaultContainerId,
String defaultContainerAlias,
boolean defaultStartContainer) {
final Map<String, ServerTemplate> serverTemplatesIds = Maps.uniqueIndex(serverTemplates,
s -> s.getId());
final Map<String, Set<String>> containerNames = Maps.transformEntries(serverTemplatesIds,
(id, server) ->
FluentIterable.from(server.getContainersSpec()).transform(c -> c.getContainerName()).toSet()
);
deploymentScreenPopupView.addServerTemplates(FluentIterable.from(serverTemplatesIds.keySet()).toSortedSet(String.CASE_INSENSITIVE_ORDER));
deploymentScreenPopupView.setValidateExistingContainerCallback(containerName -> FluentIterable.from(containerNames.get(deploymentScreenPopupView.getServerTemplate())).contains(containerName));
deploymentScreenPopupView.setContainerId(defaultContainerId);
deploymentScreenPopupView.setContainerAlias(defaultContainerAlias);
deploymentScreenPopupView.setStartContainer(defaultStartContainer);
deploymentScreenPopupView.configure(() -> {
final String containerId = deploymentScreenPopupView.getContainerId();
final String containerAlias = deploymentScreenPopupView.getContainerAlias();
final String serverTemplate = deploymentScreenPopupView.getServerTemplate();
final boolean startContainer = deploymentScreenPopupView.getStartContainer();
getSafeExecutedCommand(getBuildDeployProvisionCommand(DeploymentMode.VALIDATED,
containerId,
containerAlias,
serverTemplate,
startContainer)).execute();
deploymentScreenPopupView.hide();
});
deploymentScreenPopupView.show();
}
private Command getSafeExecutedCommand(final Command command) {
return () -> {
if (building) {
view.showABuildIsAlreadyRunning();
} else {
command.execute();
}
};
}
private Command getBuildCommand() {
return () -> {
view.showBusyIndicator(ProjectEditorResources.CONSTANTS.Building());
build();
};
}
private void build() {
building = true;
buildServiceCaller.call(getBuildSuccessCallback(),
new BuildFailureErrorCallback(view,
Collections.EMPTY_MAP)).build(projectContext.getActiveProject());
}
private RemoteCallback getBuildSuccessCallback() {
return new RemoteCallback<BuildResults>() {
@Override
public void callback(final BuildResults result) {
if (result.getErrorMessages().isEmpty()) {
notificationEvent.fire(new NotificationEvent(ProjectEditorResources.CONSTANTS.BuildSuccessful(),
NotificationEvent.NotificationType.SUCCESS));
} else {
notificationEvent.fire(new NotificationEvent(ProjectEditorResources.CONSTANTS.BuildFailed(),
NotificationEvent.NotificationType.ERROR));
}
buildResultsEvent.fire(result);
view.hideBusyIndicator();
building = false;
}
};
}
private class BuildFailureErrorCallback
extends CommandWithThrowableDrivenErrorCallback {
public BuildFailureErrorCallback(final HasBusyIndicator view,
final Map<Class<? extends Throwable>, CommandWithThrowable> commands) {
super(view,
commands);
}
@Override
public boolean error(final Message message,
final Throwable throwable) {
building = false;
return super.error(message,
throwable);
}
}
private Command getBuildDeployCommand(final DeploymentMode mode) {
return () -> {
view.showBusyIndicator(ProjectEditorResources.CONSTANTS.Building());
buildAndDeploy(mode);
};
}
private void buildAndDeploy(final DeploymentMode mode) {
building = true;
buildServiceCaller.call(getBuildSuccessCallback(),
new BuildFailureErrorCallback(view,
onBuildAndDeployGavExistsHandler)).buildAndDeploy(projectContext.getActiveProject(),
mode);
}
private Command getBuildDeployProvisionCommand(final DeploymentMode mode,
final String containerId,
final String containerAlias,
final String serverTemplate,
final boolean startContainer) {
return () -> {
view.showBusyIndicator(ProjectEditorResources.CONSTANTS.Building());
buildAndDeployAndProvision(mode,
containerId,
containerAlias,
serverTemplate,
startContainer);
};
}
private void buildAndDeployAndProvision(final DeploymentMode mode,
final String containerId,
final String containerAlias,
final String serverTemplate,
final boolean startContainer) {
building = true;
buildServiceCaller.call(getBuildDeployProvisionSuccessCallback(containerId,
containerAlias,
serverTemplate,
startContainer),
new BuildFailureErrorCallback(view,
getOnBuildAndDeployAndProvisionGavExistsHandler(containerId,
containerAlias,
serverTemplate,
startContainer))).buildAndDeploy(projectContext.getActiveProject(),
mode);
}
private RemoteCallback getBuildDeployProvisionSuccessCallback(final String containerId,
final String containerAlias,
final String serverTemplate,
final boolean startContainer) {
return new RemoteCallback<BuildResults>() {
@Override
public void callback(final BuildResults result) {
if (result.getErrorMessages().isEmpty()) {
notificationEvent.fire(new NotificationEvent(ProjectEditorResources.CONSTANTS.BuildSuccessful(),
NotificationEvent.NotificationType.SUCCESS));
if (containerId != null && serverTemplate != null) {
GAV gav = projectContext.getActiveProject().getPom().getGav();
ReleaseId releaseId = new ReleaseId(gav.getGroupId(),
gav.getArtifactId(),
gav.getVersion());
ContainerSpec containerSpec = new ContainerSpec(containerId,
containerAlias,
new ServerTemplateKey(serverTemplate,
serverTemplate),
releaseId,
KieContainerStatus.STOPPED,
new HashMap<>());
specManagementService.call(aVoid -> {
notificationEvent.fire(new NotificationEvent(ProjectEditorResources.CONSTANTS.DeploySuccessful(),
NotificationEvent.NotificationType.SUCCESS));
if (startContainer) {
specManagementService.call(aVoid1 -> {
}
).startContainer(containerSpec);
}
}).saveContainerSpec(serverTemplate,
containerSpec);
}
} else {
notificationEvent.fire(new NotificationEvent(ProjectEditorResources.CONSTANTS.BuildFailed(),
NotificationEvent.NotificationType.ERROR));
}
buildResultsEvent.fire(result);
view.hideBusyIndicator();
building = false;
}
};
}
private Map<Class<? extends Throwable>, CommandWithThrowableDrivenErrorCallback.CommandWithThrowable> getOnBuildAndDeployAndProvisionGavExistsHandler(final String containerId,
final String containerAlias,
final String serverTemplate,
final boolean startContainer) {
return new HashMap<Class<? extends Throwable>, CommandWithThrowableDrivenErrorCallback.CommandWithThrowable>() {{
put(GAVAlreadyExistsException.class,
parameter -> {
view.hideBusyIndicator();
conflictingRepositoriesPopup.setContent(projectContext.getActiveProject().getPom().getGav(),
((GAVAlreadyExistsException) parameter).getRepositories(),
() -> {
conflictingRepositoriesPopup.hide();
getBuildDeployProvisionCommand(DeploymentMode.FORCED,
containerId,
containerAlias,
serverTemplate,
startContainer).execute();
});
conflictingRepositoriesPopup.show();
});
}};
}
}