/**
* Copyright 2014 ArcBees Inc.
*
* 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 com.arcbees.staging;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.http.HttpStatus;
import org.apache.tomcat.maven.common.deployer.TomcatManager;
import org.apache.tomcat.maven.common.deployer.TomcatManagerException;
import org.apache.tomcat.maven.common.deployer.TomcatManagerResponse;
import org.jetbrains.annotations.NotNull;
import com.arcbees.vcs.VcsApi;
import com.arcbees.vcs.VcsApiFactories;
import com.arcbees.vcs.VcsConstants;
import com.arcbees.vcs.VcsPropertiesHelper;
import com.arcbees.vcs.model.Comment;
import com.arcbees.vcs.model.PullRequest;
import com.arcbees.vcs.util.JsonCustomDataStorage;
import com.google.common.collect.Lists;
import jetbrains.buildServer.buildTriggers.BuildTriggerDescriptor;
import jetbrains.buildServer.serverSide.Branch;
import jetbrains.buildServer.serverSide.CustomDataStorage;
import jetbrains.buildServer.serverSide.SBuildType;
import jetbrains.buildServer.serverSide.SRunningBuild;
import jetbrains.buildServer.serverSide.artifacts.BuildArtifact;
import jetbrains.buildServer.serverSide.artifacts.BuildArtifacts;
import jetbrains.buildServer.serverSide.artifacts.BuildArtifactsViewMode;
public class TomcatDeployHandler {
private static final Logger LOGGER = Logger.getLogger(TomcatDeployHandler.class.getName());
private static final String COMMENT_WEBAPP = "WebApp URL : ";
private final VcsApiFactories vcsApiFactories;
private final TomcatManagerFactory tomcatManagerFactory;
private final VcsConstants vcsConstants;
private final Constants constants;
public TomcatDeployHandler(VcsApiFactories vcsApiFactories,
TomcatManagerFactory tomcatManagerFactory,
VcsConstants vcsConstants,
Constants constants) {
this.vcsApiFactories = vcsApiFactories;
this.tomcatManagerFactory = tomcatManagerFactory;
this.vcsConstants = vcsConstants;
this.constants = constants;
}
public void handle(SRunningBuild build, BuildTriggerDescriptor trigger) throws IOException {
Branch branch = build.getBranch();
if (branch != null && build.getBuildStatus().isSuccessful()) {
SBuildType buildType = build.getBuildType();
VcsPropertiesHelper vcsPropertiesHelper = new VcsPropertiesHelper(trigger.getProperties(), vcsConstants);
VcsApi vcsApi = vcsApiFactories.create(vcsPropertiesHelper);
PullRequest pullRequest = vcsApi.getPullRequestForBranch(branch.getName());
JsonCustomDataStorage<TomcatStagingDeploy> dataStorage = getJsonDataStorage(buildType, trigger);
StagingPropertiesHelper stagingPropertiesHelper =
new StagingPropertiesHelper(trigger.getProperties(), constants);
TomcatStagingDeploy stagingDeploy =
getTomcatStagingDeploy(vcsPropertiesHelper, pullRequest, dataStorage);
deploy(build, stagingPropertiesHelper, stagingDeploy);
postComment(vcsApi, pullRequest, stagingDeploy);
dataStorage.putValue(getPullRequestKey(vcsPropertiesHelper, pullRequest), stagingDeploy);
}
}
private void deploy(final SRunningBuild build,
StagingPropertiesHelper stagingPropertiesHelper,
final TomcatStagingDeploy stagingDeploy) throws IOException {
final String baseContext = stagingPropertiesHelper.getBaseContext();
final TomcatManager tomcatManager = createTomcatManager(stagingPropertiesHelper);
try {
build.getArtifacts(BuildArtifactsViewMode.VIEW_DEFAULT)
.iterateArtifacts(new BuildArtifacts.BuildArtifactsProcessor() {
@NotNull
@Override
public Continuation processBuildArtifact(@NotNull BuildArtifact buildArtifact) {
if (buildArtifact.getName().endsWith(".war")) {
try {
deployArtifact(buildArtifact, build, baseContext, tomcatManager, stagingDeploy);
} catch (TomcatManagerException | IOException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new RuntimeException(e);
}
}
return Continuation.CONTINUE;
}
});
stagingDeploy.setDeployed(true);
} catch (Exception e) {
stagingDeploy.setDeployed(false);
LOGGER.log(Level.SEVERE, e.getMessage(), e);
}
}
private void deployArtifact(BuildArtifact buildArtifact,
SRunningBuild build,
String baseContext,
TomcatManager tomcatManager,
TomcatStagingDeploy stagingDeploy)
throws TomcatManagerException, IOException {
assert build.getBranch() != null;
File artifact = new File(build.getArtifactsDirectory(), buildArtifact.getRelativePath());
String path = baseContext + "/" + build.getBranch().getName();
String tomcatBasePath = UrlUtils.extractBaseUrl(tomcatManager.getURL());
stagingDeploy.setWebPath(tomcatBasePath + path);
TomcatManagerResponse response = tomcatManager.deploy(path, artifact, true, path, artifact.length());
if (HttpStatus.SC_OK != response.getStatusCode()) {
throw new TomcatManagerException(response.getReasonPhrase());
}
}
private TomcatManager createTomcatManager(StagingPropertiesHelper propertiesHelper) {
try {
return tomcatManagerFactory.create(propertiesHelper);
} catch (MalformedURLException | URISyntaxException e) {
LOGGER.log(Level.SEVERE, e.getMessage(), e);
throw new RuntimeException(e);
}
}
private Comment postComment(VcsApi vcsApi,
PullRequest pullRequest,
TomcatStagingDeploy stagingDeploy) throws IOException {
Comment comment = stagingDeploy.getComment();
if (comment == null && stagingDeploy.isDeployed()) {
comment = vcsApi.postComment(pullRequest.getId(), getComment(stagingDeploy));
stagingDeploy.setComment(comment);
}
return comment;
}
private TomcatStagingDeploy getTomcatStagingDeploy(VcsPropertiesHelper vcsPropertiesHelper,
PullRequest pullRequest,
JsonCustomDataStorage<TomcatStagingDeploy> dataStorage) {
String pullRequestKey = getPullRequestKey(vcsPropertiesHelper.getRepositoryOwner(),
vcsPropertiesHelper.getRepositoryName(), pullRequest);
TomcatStagingDeploy stagingDeploy = dataStorage.getValue(pullRequestKey);
if (stagingDeploy == null) {
stagingDeploy = new TomcatStagingDeploy(pullRequest, false);
}
return stagingDeploy;
}
private JsonCustomDataStorage<TomcatStagingDeploy> getJsonDataStorage(SBuildType buildType,
BuildTriggerDescriptor trigger) {
String storageId = getStorageId(trigger);
CustomDataStorage customDataStorage = buildType.getCustomDataStorage(storageId);
return JsonCustomDataStorage.create(customDataStorage, TomcatStagingDeploy.class);
}
private String getPullRequestKey(VcsPropertiesHelper helper, PullRequest pullRequest) {
return getPullRequestKey(helper.getRepositoryOwner(), helper.getRepositoryName(), pullRequest);
}
private String getPullRequestKey(String repositoryOwner, String repositoryName, PullRequest pullRequest) {
return vcsConstants.getPullRequestKey() + repositoryOwner + "_" + repositoryName + "_" + pullRequest
.getId();
}
private String getStorageId(BuildTriggerDescriptor triggerDescriptor) {
return triggerDescriptor.getBuildTriggerService().getClass().getName() + "_"
+ getParametersSignature(triggerDescriptor);
}
private String getParametersSignature(BuildTriggerDescriptor triggerDescriptor) {
Map<String, String> propsMap = triggerDescriptor.getParameters();
List<String> keys = Lists.newArrayList(propsMap.keySet());
Collections.sort(keys);
StringBuilder signature = new StringBuilder();
signature.append(triggerDescriptor.getType());
for (String key : keys) {
signature.append(key).append('=').append(propsMap.get(key));
}
return signature.toString();
}
private String getComment(TomcatStagingDeploy stagingDeploy) {
return COMMENT_WEBAPP + stagingDeploy.getWebPath();
}
}