/*
* Copyright 2014 JBoss 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 org.artificer.shell.maven;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.artificer.client.ArtificerAtomApiClient;
import org.artificer.client.ArtificerClientException;
import org.artificer.client.ArtificerClientQuery;
import org.artificer.common.query.ArtifactSummary;
import org.artificer.client.query.QueryResultSet;
import org.artificer.common.ArtifactType;
import org.artificer.common.ArtificerConfig;
import org.artificer.common.ArtificerModelUtils;
import org.artificer.common.error.ArtificerServerException;
import org.artificer.common.maven.MavenGavInfo;
import org.artificer.common.maven.MavenUtil;
import org.artificer.common.visitors.ArtifactVisitorHelper;
import org.artificer.integration.java.model.JavaModel;
import org.artificer.shell.common.AbstractCommand;
import org.artificer.shell.i18n.Messages;
import org.artificer.shell.util.ArtifactTypeCompleter;
import org.artificer.shell.util.FileNameCompleterDelegate;
import org.artificer.shell.util.PrintArtifactMetaDataVisitor;
import org.jboss.aesh.cl.Arguments;
import org.jboss.aesh.cl.CommandDefinition;
import org.jboss.aesh.cl.Option;
import org.jboss.aesh.cl.completer.OptionCompleter;
import org.jboss.aesh.console.command.CommandResult;
import org.jboss.aesh.console.command.completer.CompleterInvocation;
import org.jboss.aesh.console.command.invocation.CommandInvocation;
import org.oasis_open.docs.s_ramp.ns.s_ramp_v1.BaseArtifactType;
import javax.xml.bind.JAXBException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* Uploads a file to the S-RAMP repository as a new artifact. Additionally
* adds Maven meta-data to the resulting artifact, including:
*
* <ul>
* <li>Group ID</li>
* <li>Artifact ID</li>
* <li>Version</li>
* <li>Classifier (optional)</li>
* <li>Type (optional)</li>
* <li>MD5 Hash</li>
* <li>SHA1 Hash</li>
* </ul>
*
* Usage:
* <pre>
* maven:deploy <pathToFile> <groupId>:<artifactId>:<version>:[<type>]:[<classifier>] [<artifactType>]
* </pre>
*
* @author Brett Meyer
* @author eric.wittmann@redhat.com
*/
@CommandDefinition(name = "deploy",
description = "The \"deploy\" command uploads the content of a local file to the Artificer repository, creating a new artifact with appropriate maven meta-data (e.g. groupId, artifactId, version). The artifact type can also optionally be provided. If the artifact type is excluded, it will be automatically detected using contextual clues.\nExample usages\ndeploy /home/uname/files/myarchive.jar org.example:my-archive:1.0.1.Final\ndeploy /home/uname/files/other-archive.war org.example:other-archive:2.0.7.Final:war JavaWebApplication\n")
public class DeployCommand extends AbstractCommand {
private static final boolean ALLOW_SNAPSHOT = ArtificerConfig.isSnapshotAllowed();
@Arguments(description = "<file path>", completer = Completer.class)
private List<String> arguments;
@Option(name = "gav", hasValue = true, required = true,
description = "GAV")
private String gav;
@Option(name = "type", hasValue = true, required = false, completer = ArtifactTypeCompleter.class,
description = "Artifact type")
private String type;
@Override
protected String getName() {
return "maven deploy";
}
@Override
protected CommandResult doExecute(CommandInvocation commandInvocation) throws Exception {
if (CollectionUtils.isEmpty(arguments)) {
return doHelp(commandInvocation);
}
String filePathArg = requiredArgument(commandInvocation, arguments, 0);
ArtificerAtomApiClient client = client(commandInvocation);
// Validate the file
File file = new File(filePathArg);
if (!file.exists()) {
URL url = this.getClass().getClassLoader().getResource(filePathArg);
if (url != null) {
file = new File(url.toURI());
} else {
commandInvocation.getShell().out().println(Messages.i18n.format("DeployCommand.FileNotFound", filePathArg));
return CommandResult.FAILURE;
}
}
ArtifactType artifactType = null;
if (StringUtils.isNotBlank(type)) {
artifactType = ArtifactType.valueOf(type);
if (artifactType.isExtendedType()) {
artifactType = ArtifactType.ExtendedDocument(artifactType.getExtendedType());
}
}
// Process GAV and other meta-data, then update the artifact
MavenGavInfo mavenGavInfo = MavenGavInfo.fromCommandLine(gav, file);
if (mavenGavInfo.getType() == null) {
commandInvocation.getShell().out().println(Messages.i18n.format("DeployCommand.TypeNotSet", file.getName()));
return CommandResult.FAILURE;
}
if (!ALLOW_SNAPSHOT && mavenGavInfo.isSnapshot()) {
commandInvocation.getShell().out().println(Messages.i18n.format("DeployCommand.SnapshotNotAllowed", gav));
return CommandResult.FAILURE;
}
InputStream content = null;
try {
BaseArtifactType artifact = findExistingArtifactByGAV(client, mavenGavInfo);
if (artifact != null) {
commandInvocation.getShell().out().println(Messages.i18n.format("DeployCommand.Failure.ReleaseArtifact.Exist", gav));
return CommandResult.FAILURE;
} else {
content = FileUtils.openInputStream(file);
artifact = client.uploadArtifact(artifactType, content, file.getName());
}
// Process GAV and other meta-data, then update the artifact
String artifactName = mavenGavInfo.getArtifactId() + '-' + mavenGavInfo.getVersion();
String pomName = mavenGavInfo.getArtifactId() + '-' + mavenGavInfo.getVersion() + ".pom";
ArtificerModelUtils.setCustomProperty(artifact, JavaModel.PROP_MAVEN_GROUP_ID, mavenGavInfo.getGroupId());
ArtificerModelUtils.setCustomProperty(artifact, JavaModel.PROP_MAVEN_ARTIFACT_ID, mavenGavInfo.getArtifactId());
ArtificerModelUtils.setCustomProperty(artifact, JavaModel.PROP_MAVEN_VERSION, mavenGavInfo.getVersion());
ArtificerModelUtils.setCustomProperty(artifact, JavaModel.PROP_MAVEN_HASH_MD5, mavenGavInfo.getMd5());
ArtificerModelUtils.setCustomProperty(artifact, JavaModel.PROP_MAVEN_HASH_SHA1, mavenGavInfo.getSha1());
if (StringUtils.isNotBlank(mavenGavInfo.getSnapshotId())) {
ArtificerModelUtils.setCustomProperty(artifact, JavaModel.PROP_MAVEN_SNAPSHOT_ID, mavenGavInfo.getSnapshotId());
} else if (mavenGavInfo.isSnapshot()) {
ArtificerModelUtils.setCustomProperty(artifact, JavaModel.PROP_MAVEN_SNAPSHOT_ID, generateSnapshotTimestamp());
}
if (mavenGavInfo.getClassifier() != null) {
ArtificerModelUtils.setCustomProperty(artifact, "maven.classifier", mavenGavInfo.getClassifier());
artifactName += '-' + mavenGavInfo.getClassifier();
}
if (mavenGavInfo.getType() != null) {
ArtificerModelUtils.setCustomProperty(artifact, "maven.type", mavenGavInfo.getType());
artifactName += '.' + mavenGavInfo.getType();
}
artifact.setName(artifactName);
client.updateArtifactMetaData(artifact);
// Generate and add a POM for the artifact
String pom = generatePom(mavenGavInfo);
InputStream pomContent = new ByteArrayInputStream(pom.getBytes("UTF-8"));
BaseArtifactType pomArtifact = ArtifactType.ExtendedDocument(JavaModel.TYPE_MAVEN_POM_XML).newArtifactInstance();
pomArtifact.setName(pomName);
ArtificerModelUtils.setCustomProperty(pomArtifact, JavaModel.PROP_MAVEN_TYPE, "pom");
ArtificerModelUtils.setCustomProperty(pomArtifact, JavaModel.PROP_MAVEN_HASH_MD5, DigestUtils.md5Hex(pom));
ArtificerModelUtils.setCustomProperty(pomArtifact, JavaModel.PROP_MAVEN_HASH_SHA1, DigestUtils.shaHex(pom));
client.uploadArtifact(pomArtifact, pomContent);
// Put the artifact in the session as the active artifact
context(commandInvocation).setCurrentArtifact(artifact);
commandInvocation.getShell().out().println(Messages.i18n.format("DeployCommand.Success"));
PrintArtifactMetaDataVisitor visitor = new PrintArtifactMetaDataVisitor(commandInvocation);
ArtifactVisitorHelper.visitArtifact(visitor, artifact);
return CommandResult.SUCCESS;
} catch (Exception e) {
commandInvocation.getShell().out().println(Messages.i18n.format("DeployCommand.Failure"));
commandInvocation.getShell().out().println("\t" + e.getMessage());
return CommandResult.FAILURE;
} finally {
IOUtils.closeQuietly(content);
}
}
/**
* Generates a simple maven pom given the artifact information.
*
* @param mavenGavInfo
* @return a generated Maven pom
*/
private String generatePom(MavenGavInfo mavenGavInfo) {
StringBuilder builder = new StringBuilder();
builder.append("<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n");
builder.append(" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\r\n");
builder.append(" <modelVersion>4.0.0</modelVersion>\r\n");
builder.append(" <groupId>" + mavenGavInfo.getGroupId() + "</groupId>\r\n");
builder.append(" <artifactId>" + mavenGavInfo.getArtifactId() + "</artifactId>\r\n");
builder.append(" <version>" + mavenGavInfo.getVersion() + "</version>\r\n");
if (mavenGavInfo.getType() != null) {
builder.append(" <packaging>" + mavenGavInfo.getType() + "</packaging>\r\n");
}
if (mavenGavInfo.getClassifier() != null) {
builder.append(" <classifier>" + mavenGavInfo.getClassifier() + "</classifier>\r\n");
}
builder.append("</project>");
return builder.toString();
}
private static class Completer implements OptionCompleter<CompleterInvocation> {
@Override
public void complete(CompleterInvocation completerInvocation) {
DeployCommand command = (DeployCommand) completerInvocation.getCommand();
if (CollectionUtils.isEmpty(command.arguments)) {
FileNameCompleterDelegate.complete(completerInvocation);
}
}
}
/**
* Finds an existing artifact in the s-ramp repository that matches the GAV
* information.
*
* @param client
* the client
* @param mavenGavInfo
* @return an s-ramp artifact (if found) or null (if not found)
* @throws org.artificer.atom.err.ArtificerAtomException
* the sramp atom exception
* @throws org.artificer.client.ArtificerClientException
* the sramp client exception
* @throws javax.xml.bind.JAXBException
* the JAXB exception
*/
private BaseArtifactType findExistingArtifactByGAV(ArtificerAtomApiClient client, MavenGavInfo mavenGavInfo) throws ArtificerServerException,
ArtificerClientException, JAXBException {
String query = MavenUtil.gavQuery(mavenGavInfo);
ArtificerClientQuery clientQuery = client.buildQuery(query);
QueryResultSet rset = clientQuery.count(100).query();
if (rset.size() > 0) {
for (ArtifactSummary summary : rset) {
String uuid = summary.getUuid();
ArtifactType artifactType = summary.getArtifactType();
BaseArtifactType arty = client.getArtifactMetaData(artifactType, uuid);
return arty;
}
}
return null;
}
/**
* Generate snapshot timestamp.
*
* @return the string
*/
private String generateSnapshotTimestamp() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd.hhmmss");
String timestamp = sdf.format(new Date());
StringBuilder builder = new StringBuilder();
// It is added at the end the maven counter. By default it is set to
// "1". The maven format for the timestamp is yyyyMMdd.hhmmss-counter
builder.append(timestamp).append("-1");
return builder.toString();
}
}