/*
* Copyright 2013 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.rest.backend;
import java.lang.annotation.Annotation;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.inject.Named;
import org.guvnor.common.services.project.builder.model.BuildMessage;
import org.guvnor.common.services.project.builder.model.BuildResults;
import org.guvnor.common.services.project.builder.service.BuildService;
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.model.Project;
import org.guvnor.common.services.project.service.GAVAlreadyExistsException;
import org.guvnor.common.services.project.service.ProjectService;
import org.guvnor.common.services.shared.test.TestResultMessage;
import org.guvnor.common.services.shared.test.TestService;
import org.guvnor.rest.client.JobResult;
import org.guvnor.rest.client.JobStatus;
import org.guvnor.rest.client.RepositoryRequest;
import org.guvnor.structure.organizationalunit.OrganizationalUnit;
import org.guvnor.structure.organizationalunit.OrganizationalUnitService;
import org.guvnor.structure.organizationalunit.impl.OrganizationalUnitImpl;
import org.guvnor.structure.repositories.RepositoryEnvironmentConfigurations;
import org.guvnor.structure.repositories.RepositoryService;
import org.guvnor.structure.repositories.impl.git.GitRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.uberfire.backend.server.util.Paths;
import org.uberfire.backend.vfs.Path;
import org.uberfire.io.IOService;
/**
* Utility class to perform various functions for the REST service involving backend services
*/
@ApplicationScoped
public class JobRequestHelper {
public static final String GUVNOR_BASE_URL = "/";
private static final Logger logger = LoggerFactory.getLogger(JobRequestHelper.class);
@Inject
private RepositoryService repositoryService;
@Inject
private ProjectService<? extends Project> projectService;
@Inject
private BuildService buildService;
@Inject
@Named("ioStrategy")
private IOService ioSystemService;
@Inject
private OrganizationalUnitService organizationalUnitService;
@Inject
private TestService testService;
public JobResult createOrCloneRepository(final String jobId,
final RepositoryRequest repository) {
JobResult result = new JobResult();
result.setJobId(jobId);
if (repository.getRequestType() == null || "".equals(repository.getRequestType())
|| !("new".equals(repository.getRequestType()) || ("clone".equals(repository.getRequestType())))) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("Repository request type can only be new or clone.");
return result;
}
final String scheme = "git";
String orgUnitName = repository.getOrganizationalUnitName();
OrganizationalUnit orgUnit = organizationalUnitService.getOrganizationalUnit(repository.getOrganizationalUnitName());
if (orgUnit == null) {
// double check, this is also checked at input
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("Organizational unit '" + orgUnitName + "' does not exist!");
return result;
}
if ("new".equals(repository.getRequestType())) {
if (repository.getName() == null || "".equals(repository.getName())) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("Repository name must be provided");
return result;
}
// username and password are optional
final RepositoryEnvironmentConfigurations configuration = new RepositoryEnvironmentConfigurations();
if (repository.getUserName() != null && !"".equals(repository.getUserName())) {
configuration.setUserName(repository.getUserName());
}
if (repository.getPassword() != null && !"".equals(repository.getPassword())) {
configuration.setPassword(repository.getPassword());
}
configuration.setInit(true);
org.guvnor.structure.repositories.Repository newlyCreatedRepo = repositoryService.createRepository(
orgUnit,
scheme,
repository.getName(),
configuration);
if (newlyCreatedRepo != null) {
result.setStatus(JobStatus.SUCCESS);
result.setResult("Alias: " + newlyCreatedRepo.getAlias() + ", Scheme: " + newlyCreatedRepo.getScheme() + ", Uri: " + newlyCreatedRepo.getUri());
} else {
result.setStatus(JobStatus.FAIL);
}
} else if ("clone".equals(repository.getRequestType())) {
if (repository.getName() == null || "".equals(repository.getName()) || repository.getGitURL() == null
|| "".equals(repository.getGitURL())) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("Repository name and GitURL must be provided");
}
// username and password are optional
final RepositoryEnvironmentConfigurations configuration = new RepositoryEnvironmentConfigurations();
if (repository.getUserName() != null && !"".equals(repository.getUserName())) {
configuration.setUserName(repository.getUserName());
}
if (repository.getPassword() != null && !"".equals(repository.getPassword())) {
configuration.setPassword(repository.getPassword());
}
configuration.setOrigin(repository.getGitURL());
org.guvnor.structure.repositories.Repository newlyCreatedRepo = repositoryService.createRepository(
orgUnit,
scheme,
repository.getName(),
configuration);
if (newlyCreatedRepo != null) {
result.setStatus(JobStatus.SUCCESS);
result.setResult("Alias: " + newlyCreatedRepo.getAlias() + ", Scheme: " + newlyCreatedRepo.getScheme() + ", Uri: " + newlyCreatedRepo.getUri());
} else {
result.setStatus(JobStatus.FAIL);
}
}
return result;
}
public JobResult removeRepository(final String jobId,
final String repositoryName) {
JobResult result = new JobResult();
result.setJobId(jobId);
if (repositoryName == null || "".equals(repositoryName)) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("Repository name must be provided");
return result;
}
repositoryService.removeRepository(repositoryName);
result.setStatus(JobStatus.SUCCESS);
return result;
}
public JobResult createProject(final String jobId,
final String repositoryAlias,
final String projectName,
String projectGroupId,
String projectVersion,
String projectDescription) {
JobResult result = new JobResult();
result.setJobId(jobId);
org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (projectGroupId == null || projectGroupId.trim().isEmpty()) {
projectGroupId = projectName;
}
if (projectVersion == null || projectVersion.trim().isEmpty()) {
projectVersion = "1.0";
}
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
} else {
POM pom = new POM();
pom.getGav().setArtifactId(projectName);
pom.getGav().setGroupId(projectGroupId);
pom.getGav().setVersion(projectVersion);
pom.setDescription(projectDescription);
pom.setName(projectName);
try {
projectService.newProject(Paths.convert(repositoryPath),
pom,
GUVNOR_BASE_URL);
} catch (GAVAlreadyExistsException gae) {
result.setStatus(JobStatus.DUPLICATE_RESOURCE);
result.setResult("Project's GAV [" + gae.getGAV().toString() + "] already exists at [" + toString(gae.getRepositories()) + "]");
return result;
} catch (org.uberfire.java.nio.file.FileAlreadyExistsException e) {
result.setStatus(JobStatus.DUPLICATE_RESOURCE);
result.setResult("Project [" + projectName + "] already exists");
return result;
}
result.setStatus(JobStatus.SUCCESS);
return result;
}
}
private String toString(final Set<MavenRepositoryMetadata> repositories) {
final StringBuilder sb = new StringBuilder();
for (MavenRepositoryMetadata md : repositories) {
sb.append(md.getId()).append(" : ").append(md.getUrl()).append(" : ").append(md.getSource()).append(", ");
}
sb.delete(sb.length() - 2,
sb.length() - 1);
return sb.toString();
}
public JobResult deleteProject(String jobId,
String repositoryAlias,
String projectName) {
JobResult result = new JobResult();
result.setJobId(jobId);
org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
} else {
String repoPathStr = repositoryPath.toUri().toString();
StringBuilder projectPomUriStrBdr = new StringBuilder(repoPathStr);
if (!repoPathStr.endsWith("/")) {
projectPomUriStrBdr.append("/");
}
projectPomUriStrBdr.append(projectName).append("/pom.xml");
URI projectPomUri = URI.create(projectPomUriStrBdr.toString());
Path projectPomPath = Paths.convert(org.uberfire.java.nio.file.Paths.get(projectPomUri));
try {
projectService.delete(projectPomPath,
"Deleting project via REST request");
} catch (Exception e) {
result.setStatus(JobStatus.FAIL);
result.setResult("Project [" + projectName + "] could not be deleted: " + e.getMessage());
logger.error("Unable to delete project '" + projectName + "': " + e.getMessage(),
e);
return result;
}
result.setStatus(JobStatus.SUCCESS);
return result;
}
}
public JobResult compileProject(final String jobId,
final String repositoryAlias,
final String projectName) {
JobResult result = new JobResult();
result.setJobId(jobId);
org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
} else {
Project project = projectService.resolveProject(Paths.convert(repositoryPath.resolve(projectName)));
if (project == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Project [" + projectName + "] does not exist");
return result;
}
BuildResults buildResults = buildService.build(project);
result.setDetailedResult(buildResultsToDetailedStringMessages(buildResults.getMessages()));
result.setStatus(buildResults.getErrorMessages().isEmpty() ? JobStatus.SUCCESS : JobStatus.FAIL);
return result;
}
}
private List<String> buildResultsToDetailedStringMessages(List<BuildMessage> messages) {
List<String> result = new ArrayList<String>();
for (BuildMessage message : messages) {
String detailedStringMessage = "level:" + message.getLevel() +
", path:" + message.getPath() +
", text:" + message.getText();
result.add(detailedStringMessage);
}
return result;
}
public JobResult installProject(final String jobId,
final String repositoryAlias,
final String projectName) {
JobResult result = new JobResult();
result.setJobId(jobId);
org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
} else {
Project project = projectService.resolveProject(Paths.convert(repositoryPath.resolve(projectName)));
if (project == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Project [" + projectName + "] does not exist");
return result;
}
BuildResults buildResults = null;
try {
buildResults = buildService.buildAndDeploy(project);
result.setDetailedResult(buildResults == null ? null : deployResultToDetailedStringMessages(buildResults));
result.setStatus(buildResults != null && buildResults.getErrorMessages().isEmpty() ? JobStatus.SUCCESS : JobStatus.FAIL);
} catch (Throwable t) {
Optional<GAVAlreadyExistsException> gaeOpt = findCause(t, GAVAlreadyExistsException.class);
if (gaeOpt.isPresent()) {
GAVAlreadyExistsException gae = gaeOpt.get();
result.setStatus(JobStatus.DUPLICATE_RESOURCE);
result.setResult("Project's GAV [" + gae.getGAV() + "] already exists at [" + toString(gae.getRepositories()) + "]");
} else {
List<String> errorResult = new ArrayList<String>();
errorResult.add(t.getMessage());
result.setDetailedResult(errorResult);
result.setStatus(JobStatus.FAIL);
}
}
return result;
}
}
private List<String> deployResultToDetailedStringMessages(final BuildResults deployResult) {
GAV gav = deployResult.getGAV();
List<String> result = buildResultsToDetailedStringMessages(deployResult.getErrorMessages());
String detailedStringMessage = "artifactID:" + gav.getArtifactId() +
", groupId:" + gav.getGroupId() +
", version:" + gav.getVersion();
result.add(detailedStringMessage);
return result;
}
public JobResult testProject(final String jobId,
final String repositoryAlias,
final String projectName) {
final JobResult result = new JobResult();
result.setJobId(jobId);
final org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
} else {
final Project project = projectService.resolveProject(Paths.convert(repositoryPath.resolve(projectName)));
if (project == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Project [" + projectName + "] does not exist");
return result;
} else {
testService.runAllTests(
"JobRequestHelper",
project.getPomXMLPath(),
getCustomTestResultEvent(result));
return result;
}
}
}
private Event<TestResultMessage> getCustomTestResultEvent(final JobResult result) {
return new Event<TestResultMessage>() {
@Override
public void fire(TestResultMessage event) {
result.setDetailedResult(event.getResultStrings());
result.setStatus(event.wasSuccessful() ? JobStatus.SUCCESS : JobStatus.FAIL);
}
@Override
public Event<TestResultMessage> select(Annotation... qualifiers) {
// not used
return null;
}
@Override
public <U extends TestResultMessage> Event<U> select(Class<U> subtype,
Annotation... qualifiers) {
// not used
return null;
}
};
}
public JobResult deployProject(final String jobId,
final String repositoryAlias,
final String projectName) {
JobResult result = new JobResult();
result.setJobId(jobId);
org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
} else {
Project project = projectService.resolveProject(Paths.convert(repositoryPath.resolve(projectName)));
if (project == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Project [" + projectName + "] does not exist");
return result;
}
BuildResults buildResults = null;
try {
buildResults = buildService.buildAndDeploy(project);
result.setDetailedResult(buildResults == null ? null : deployResultToDetailedStringMessages(buildResults));
result.setStatus(buildResults != null && buildResults.getErrorMessages().isEmpty() ? JobStatus.SUCCESS : JobStatus.FAIL);
} catch (RuntimeException ex) {
GAVAlreadyExistsException gae = findCause(ex, GAVAlreadyExistsException.class).orElseThrow(() -> ex);
result.setStatus(JobStatus.DUPLICATE_RESOURCE);
result.setResult("Project's GAV [" + gae.getGAV() + "] already exists at [" + toString(gae.getRepositories()) + "]");
return result;
}
return result;
}
}
public JobResult removeOrganizationalUnit(final String jobId,
final String organizationalUnitName) {
JobResult result = new JobResult();
result.setJobId(jobId);
if (organizationalUnitName == null) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("OrganizationalUnit name must be provided");
return result;
}
try {
organizationalUnitService.removeOrganizationalUnit(organizationalUnitName);
result.setStatus(JobStatus.SUCCESS);
} catch (Exception e) {
result.setStatus(JobStatus.FAIL);
String errMsg = e.getClass().getSimpleName() + " thrown when trying to remove '" + organizationalUnitName + "': " + e.getMessage();
result.setResult(errMsg);
logger.error(errMsg,
e);
}
return result;
}
public JobResult createOrganizationalUnit(final String jobId,
final String organizationalUnitName,
final String organizationalUnitOwner,
final String defaultGroupId,
final List<String> repositoryNameList) {
JobResult result = new JobResult();
result.setJobId(jobId);
if (organizationalUnitName == null || organizationalUnitOwner == null) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("OrganizationalUnit name and owner must be provided");
return result;
}
String _defaultGroupId = null;
if (defaultGroupId == null || defaultGroupId.trim().isEmpty()) {
_defaultGroupId = organizationalUnitService.getSanitizedDefaultGroupId(organizationalUnitName);
logger.warn("No default group id was provided, reverting to the organizational unit name");
} else {
if (!organizationalUnitService.isValidGroupId(defaultGroupId)) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("Invalid default group id, only alphanumerical characters are admitted, " +
"as well as '\"_\"', '\"-\"' or '\".\"'.");
return result;
} else {
_defaultGroupId = defaultGroupId;
}
}
OrganizationalUnit organizationalUnit = organizationalUnitService.getOrganizationalUnit(organizationalUnitName);
if (organizationalUnit != null) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("OrganizationalUnit with name " + organizationalUnitName + " already exists");
return result;
}
List<org.guvnor.structure.repositories.Repository> repositories = new ArrayList<org.guvnor.structure.repositories.Repository>();
if (repositoryNameList != null && repositoryNameList.size() > 0) {
for (String repositoryAlias : repositoryNameList) {
org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
}
GitRepository repo = new GitRepository(repositoryAlias);
repositories.add(repo);
}
organizationalUnit = organizationalUnitService.createOrganizationalUnit(organizationalUnitName,
organizationalUnitOwner,
_defaultGroupId,
repositories);
} else {
organizationalUnit = organizationalUnitService.createOrganizationalUnit(organizationalUnitName,
organizationalUnitOwner,
_defaultGroupId);
}
if (organizationalUnit != null) {
result.setResult("OrganizationalUnit " + organizationalUnit.getName() + " is created successfully.");
result.setStatus(JobStatus.SUCCESS);
} else {
result.setStatus(JobStatus.FAIL);
}
return result;
}
public JobResult updateOrganizationalUnit(final String jobId,
final String organizationalUnitName,
final String organizationalUnitOwner,
final String defaultGroupId) {
JobResult result = new JobResult();
result.setJobId(jobId);
if (organizationalUnitName == null || organizationalUnitOwner == null) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("OrganizationalUnit name and owner must be provided");
return result;
}
String _defaultGroupId = null;
if (defaultGroupId == null || defaultGroupId.trim().isEmpty()) {
_defaultGroupId = organizationalUnitService.getSanitizedDefaultGroupId(organizationalUnitName);
logger.warn("No default group id was provided, reverting to the organizational unit name");
} else {
if (!organizationalUnitService.isValidGroupId(defaultGroupId)) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("Invalid default group id, only alphanumerical characters are admitted, " +
"as well as '\"_\"', '\"-\"' or '\".\"'.");
return result;
} else {
_defaultGroupId = defaultGroupId;
}
}
OrganizationalUnit organizationalUnit = organizationalUnitService.updateOrganizationalUnit(organizationalUnitName,
organizationalUnitOwner,
_defaultGroupId);
if (organizationalUnit != null) {
result.setResult("OrganizationalUnit " + organizationalUnit.getName() + " was successfully updated.");
result.setStatus(JobStatus.SUCCESS);
} else {
result.setStatus(JobStatus.FAIL);
}
return result;
}
public JobResult addRepositoryToOrganizationalUnit(final String jobId,
final String organizationalUnitName,
final String repositoryAlias) {
JobResult result = new JobResult();
result.setJobId(jobId);
if (organizationalUnitName == null || repositoryAlias == null) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("OrganizationalUnit name and Repository name must be provided");
return result;
}
org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
}
OrganizationalUnit organizationalUnit = new OrganizationalUnitImpl(organizationalUnitName,
null,
null);
GitRepository repo = new GitRepository(repositoryAlias);
try {
organizationalUnitService.addRepository(organizationalUnit,
repo);
} catch (IllegalArgumentException e) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("OrganizationalUnit " + organizationalUnit.getName() + " not found");
return result;
}
result.setStatus(JobStatus.SUCCESS);
return result;
}
public JobResult removeRepositoryFromOrganizationalUnit(final String jobId,
final String organizationalUnitName,
final String repositoryAlias) {
JobResult result = new JobResult();
result.setJobId(jobId);
if (organizationalUnitName == null || repositoryAlias == null) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("OrganizationalUnit name and Repository name must be provided");
return result;
}
org.uberfire.java.nio.file.Path repositoryPath = getRepositoryRootPath(repositoryAlias);
if (repositoryPath == null) {
result.setStatus(JobStatus.RESOURCE_NOT_EXIST);
result.setResult("Repository [" + repositoryAlias + "] does not exist");
return result;
}
OrganizationalUnit organizationalUnit = new OrganizationalUnitImpl(organizationalUnitName,
null,
null);
GitRepository repo = new GitRepository(repositoryAlias);
try {
organizationalUnitService.removeRepository(organizationalUnit,
repo);
} catch (IllegalArgumentException e) {
result.setStatus(JobStatus.BAD_REQUEST);
result.setResult("OrganizationalUnit " + organizationalUnit.getName() + " not found");
return result;
}
result.setStatus(JobStatus.SUCCESS);
return result;
}
private org.uberfire.java.nio.file.Path getRepositoryRootPath(final String repositoryAlias) {
org.guvnor.structure.repositories.Repository repository = repositoryService.getRepository(repositoryAlias);
if (repository == null) {
return null;
}
return Paths.convert(repository.getBranchRoot(repository.getDefaultBranch()));
}
private <T> Optional<T> findCause(Throwable t, Class<T> causeClass) {
if (t == null) {
return Optional.empty();
} else if (t.getClass().equals(causeClass)) {
return Optional.of((T)t);
} else {
return findCause(t.getCause(), causeClass);
}
}
}