package org.kevoree.tools.mavenplugin;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.typesafe.config.Config;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.kevoree.ContainerRoot;
import org.kevoree.DeployUnit;
import org.kevoree.TypeDefinition;
import org.kevoree.bootstrap.util.ConfigHelper;
import org.kevoree.factory.DefaultKevoreeFactory;
import org.kevoree.factory.KevoreeFactory;
import org.kevoree.pmodeling.api.KMFContainer;
import org.kevoree.pmodeling.api.ModelCloner;
import org.kevoree.pmodeling.api.ModelLoader;
import org.kevoree.pmodeling.api.ModelSerializer;
import org.kevoree.pmodeling.api.compare.ModelCompare;
import org.kevoree.pmodeling.api.trace.TraceSequence;
import org.kevoree.registry.api.OAuthRegistryClient;
import org.kevoree.registry.api.RegistryRestClient;
import org.kevoree.registry.api.model.TypeDef;
import org.kevoree.tools.mavenplugin.util.ModelBuilderHelper;
import org.kevoree.tools.mavenplugin.util.RegistryHelper;
import java.io.File;
import java.io.FileInputStream;
import java.net.MalformedURLException;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* Created by duke on 8/27/14.
*/
@Mojo(name = "deploy", defaultPhase = LifecyclePhase.DEPLOY, requiresDependencyResolution = ResolutionScope.COMPILE)
public class KevDeployMojo extends AbstractMojo {
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;
@Parameter(defaultValue = "${project.build.directory}/classes/KEV-INF/kevlib.json", required = true)
private File model;
@Parameter()
private String registry = null;
@Parameter()
private String login = null;
@Parameter()
private String password = null;
@Parameter(required = true)
private String namespace;
private KevoreeFactory factory = new DefaultKevoreeFactory();
private ModelLoader loader = factory.createJSONLoader();
private ModelSerializer serializer = factory.createJSONSerializer();
private ModelCloner cloner = factory.createModelCloner();
private ModelCompare compare = factory.createModelCompare();
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (project.getArtifact().getType().equals("jar")) {
Config config = ConfigHelper.get();
try {
this.registry = RegistryHelper.getUrl(config, registry).toString();
} catch (MalformedURLException e) {
throw new MojoExecutionException("Kevoree registry URL is malformed", e);
}
if (login == null || login.isEmpty()) {
login = config.getString("user.login");
}
if (password == null || login.isEmpty()) {
password = config.getString("user.password");
}
this.getLog().info("Registry: " + registry);
this.getLog().info("Namespace: " + namespace);
this.getLog().info("Login: " + login);
if (model != null && model.exists()) {
try (final FileInputStream fis = new FileInputStream(model)) {
processModel((ContainerRoot) loader.loadModelFromStream(fis).get(0));
} catch (MojoExecutionException e) {
this.getLog().error(e);
throw e;
} catch (final Exception e) {
getLog().error(e);
throw new MojoExecutionException("Unable to load model from file \"" + model + "\". Did you manually modified the file?");
}
} else {
throw new MojoExecutionException("Model file \""+model+"\" not found.");
}
}
}
private void processModel(final ContainerRoot model) throws MojoExecutionException {
this.getLog().info("Model: " + this.model);
List<KMFContainer> tdefs = model.select("**/typeDefinitions[]");
if (tdefs.isEmpty()) {
throw new MojoExecutionException("You must define at least one TypeDefinition. None found.");
} else {
ContainerRoot clonedModel = cloner.clone(model);
for (KMFContainer elem : tdefs) {
processTypeDefinition((TypeDefinition) clonedModel.findByPath(elem.path()));
}
String deployUnitPath = "";
for (String subPath: namespace.split("\\.")) {
deployUnitPath += "/packages[" + subPath + "]";
}
String hashcode = ModelBuilderHelper.createKey(namespace, project.getArtifactId(), project.getVersion(), null);
deployUnitPath += "/deployUnits[hashcode="+hashcode+",name=" + project.getArtifactId() + ",version=" + project.getVersion() + "]";
this.getLog().debug("Considering the pom.xml and namespace given we are supposed to find this DeployUnit in the model:");
this.getLog().debug(" -> " + deployUnitPath);
DeployUnit du = (DeployUnit) model.findByPath(deployUnitPath);
if (du != null) {
// select all typeDefs that are related to this DeployUnit
tdefs = tdefs.stream().filter(tdef ->
!tdef.select("deployUnits[hashcode="+du.getHashcode()+",name="+du.getName()+",version="+du.getVersion()+"]").isEmpty()).collect(Collectors.toList());
processDeployUnit(cloner.clone(model), tdefs, du);
} else {
throw new MojoExecutionException("Unable to find DeployUnit " + deployUnitPath + " in " + this.model.getPath());
}
}
}
private void processTypeDefinition(final TypeDefinition tdef) throws MojoExecutionException {
String tdefStr;
tdef.removeAllDeployUnits();
try {
tdefStr = serializer.serialize(tdef).trim();
} catch (Exception e) {
throw new MojoExecutionException("Unable to serialize TypeDefinition " + namespace + "." + tdef.getName() + "/" + tdef.getVersion());
}
this.getLog().info("");
this.getLog().info("Looking for TypeDefinition " + namespace + "." + tdef.getName() + "/" + tdef.getVersion() + " in the registry...");
try {
TypeDef regTdef = new RegistryRestClient(registry, null).getTypeDef(namespace, tdef.getName(), tdef.getVersion());
if (regTdef != null) {
// typeDef exists
this.getLog().info("Found (id:"+ regTdef.getId() +")");
String registryTypeDef = regTdef.getModel();
TypeDefinition regTypeDefinition = (TypeDefinition) loader.loadModelFromString(registryTypeDef).get(0);
TypeDefinition localTdef = (TypeDefinition) loader.loadModelFromString(serializer.serialize(tdef)).get(0);
// debug traces
this.getLog().debug("");
this.getLog().debug("Registry TypeDefinition:");
this.getLog().debug(serializer.serialize(regTypeDefinition));
this.getLog().debug("");
this.getLog().debug("Local TypeDefinition:");
this.getLog().debug(serializer.serialize(localTdef));
TraceSequence diffSeq = compare.diff(regTypeDefinition, localTdef);
if (!diffSeq.getTraces().isEmpty()) {
// there are discrepencies between local & registry
printDiff(regTypeDefinition, tdef, diffSeq);
throw new MojoExecutionException("If you want to use your local changes then you have to increment the version of " + namespace + "." + tdef.getName());
}
} else {
// typeDef does not exist
this.getLog().info("Not found, creating...");
this.getLog().info("");
String accessToken = new OAuthRegistryClient(registry).getToken(login, password);
HttpResponse<JsonNode> resp = new RegistryRestClient(registry, accessToken)
.postTypeDef(namespace, tdefStr, tdef.getName(), tdef.getVersion());
if (resp.getStatus() == 401) {
throw new MojoExecutionException("You are not logged in");
} else if (resp.getStatus() == 403) {
throw new MojoExecutionException("You are not a member of namespace \""+namespace+"\"");
} else if (resp.getStatus() == 404) {
throw new MojoExecutionException("Namespace \""+namespace+"\" does not exist in the registry");
} else if (resp.getStatus() == 201) {
this.getLog().info("");
this.getLog().info("Success: " + namespace + "." + tdef.getName() + "/" + tdef.getVersion() + " published on registry");
} else {
throw new MojoExecutionException("Unable to publish TypeDefinition (status=" + resp.getStatus() + ", statusText="+resp.getStatusText() + ")");
}
}
} catch (UnirestException e) {
throw new MojoExecutionException("Something went wrong with the registry client", e);
}
}
private void printDiff(TypeDefinition regTypeDef, TypeDefinition localTypeDef, TraceSequence seq) {
// TODO pretty print the diff
this.getLog().error("There are discrepencies between local & registry version of TypeDefinition " + namespace + "." + localTypeDef.getName());
seq.getTraces().forEach(trace -> {
this.getLog().warn(trace.toString());
});
}
private void processDeployUnit(ContainerRoot model, List<KMFContainer> tdefs, DeployUnit du) throws MojoExecutionException {
String platform = du.findFiltersByID("platform").getValue();
// clean model
model.select("**/typeDefinitions[]").forEach(tdef -> tdef.delete());
// serialize model
String duModelStr = serializer.serialize(model);
try {
// auth
String accessToken = new OAuthRegistryClient(registry).getToken(login, password);
RegistryRestClient client = new RegistryRestClient(registry, accessToken);
getLog().info("");
for (KMFContainer elem : tdefs) {
TypeDefinition tdef = (TypeDefinition) elem;
// then for each TypeDef: update DeployUnit
getLog().info("Looking for DeployUnit " + du.getName() + "/" + du.getVersion() + "/" + platform + " in the registry...");
org.kevoree.registry.api.model.DeployUnit regDu = client.getDeployUnit(namespace, tdef.getName(), tdef.getVersion(), platform, du.getName(), du.getVersion());
if (regDu != null) {
getLog().info("Found (id:" + regDu.getId() + ")");
regDu.setModel(duModelStr);
// update DeployUnit
HttpResponse<JsonNode> resp = client.putDeployUnit(regDu);
if (resp.getStatus() == 200) {
// updated
getLog().info("Successfully updated");
} else {
if (resp.getBody() != null && resp.getBody().getObject().get("message") != null) {
throw new MojoExecutionException("Unable to update DeployUnit (status=" + resp.getStatus() + ", statusText=" + resp.getStatusText()+", message="+resp.getBody().getObject().get("message")+")");
} else {
throw new MojoExecutionException("Unable to update DeployUnit (status=" + resp.getStatus() + ", statusText=" + resp.getStatusText()+")");
}
}
} else {
// no DeployUnit found yet: create it
HttpResponse<JsonNode> resp = client.postDeployUnit(namespace, tdef.getName(), tdef.getVersion(), platform, duModelStr, du.getName(), du.getVersion());
if (resp.getStatus() == 201) {
// created
getLog().info("Successfully created");
} else {
throw new MojoExecutionException("Unable to create DeployUnit (status=" + resp.getStatus() + ", statusText=" + resp.getStatusText()+")");
}
}
}
} catch (UnirestException e) {
throw new MojoExecutionException("Something went wrong with the registry client", e);
}
}
}