package org.easysoa.discovery.code;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.easysoa.discovery.code.handler.JaxRSSourcesHandler;
import org.easysoa.discovery.code.handler.JaxWSSourcesHandler;
import org.easysoa.discovery.code.handler.SourcesHandler;
import org.easysoa.registry.rest.OperationResult;
import org.easysoa.registry.rest.RegistryApi;
import org.easysoa.registry.rest.SoaNodeInformation;
import org.easysoa.registry.rest.client.ClientBuilder;
import org.easysoa.registry.rest.client.types.java.MavenDeliverableInformation;
import org.easysoa.registry.types.Deliverable;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.thoughtworks.qdox.JavaDocBuilder;
import com.thoughtworks.qdox.model.JavaSource;
/**
* Allows to discover services information by parsing the sources of a project.
* All discoveries are then sent to Nuxeo through its REST API.
*
* @goal discover
*/
public class CodeDiscoveryMojo extends AbstractMojo {
/**
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject project;
/**
* @parameter default-value="http://localhost:8080/nuxeo/site"
*/
private String nuxeoSitesUrl;
/**
* @parameter default-value="Administrator"
*/
private String username;
/**
* @parameter default-value="Administrator"
*/
private String password;
/**
* @parameter default-value="/default-domain/MyProject/Realisation_v"
*/
private String subproject;
/**
* @parameter
*/
private String application;
/**
* The current SCM sources version (commit hash)
*
* @parameter expression="${buildNumber}"
*/
private String buildNumber;
/**
* May be set to false to always create InformationServices out of Java interfaces
* (i.e. source code is the "master" controlling the InformationService layer).
* Otherwise (by default), interfaces are only created if an existing
* matching one is not found (i.e. the "master" controlling the InformationService
* layer is not source code but ex. manual Specifications subproject in the registry).
*
* @parameter expression="${easysoa-discovery-code.matchInterfacesFirst}" default-value="true"
*/
private boolean matchInterfacesFirst;
/**
* May be set to false to altogether disable discovering InformationServices
* out of Java interfaces.
* Set by default.
*
* @parameter expression="${easysoa-discovery-code.discoverInterfaces}" default-value="true"
*/
private boolean discoverInterfaces;
/**
* May be set to false to disable discovery of Java service implementations
* ex. when initializing an EasySOA project's Specifications subproject with
* InformationServices out of existing Java API code (then discoverInterfaces will be set).
* Set by default.
*
* @parameter expression="${easysoa-discovery-code.discoverImplementations}" default-value="true"
*/
private boolean discoverImplementations;
/**
* The regex to match by group:artifact id where interfaces have to be
* looked up. By default look up interfaces in top-level pom project group
* (if no parent, means to project's group id).
*
* @parameter
*/
private String interfaceLookupMavenIdRegex = null;
private Map<String, SourcesHandler> availableHandlers = new HashMap<String, SourcesHandler>();
/** Cached compiled pattern of interfaceLookupMavenIdRegex, inited in getter */
private Pattern interfaceLookupMavenIdRegexPattern = null;
public void execute() throws MojoExecutionException {
Log log = getLog();
try {
if ("pom".equals(this.getMavenProject().getArtifact().getType())) {
log.info("Skipping project " + this.getMavenProject().getArtifact().getId() + " (pom so no source)");
return;
}
if (interfaceLookupMavenIdRegex == null) {
// By default look up interfaces in top-level pom project group
// (so if no parent, by default look up interfaces in the same
// group)
MavenProject topLevelMavenProject = getTopLevelMavenProject(this.getMavenProject());
interfaceLookupMavenIdRegex = topLevelMavenProject.getArtifact().getGroupId() + ":.*";
}
try {
this.interfaceLookupMavenIdRegexPattern = Pattern.compile(this.interfaceLookupMavenIdRegex);
// compiling the regex to match by group:artifact id where
// interfaces have to be looked up
} catch (Throwable error) {
log.info("Error compiling pattern " + this.interfaceLookupMavenIdRegex
+ ", should be a valid regex, aborting.");
log.debug(error);
return;
}
// Init registry client
ClientBuilder clientBuilder = new ClientBuilder();
clientBuilder.setCredentials(username, password);
clientBuilder.setNuxeoSitesUrl(nuxeoSitesUrl);
RegistryApi registryApi = clientBuilder.constructRegistryApi();
// Init handlers
this.availableHandlers.put("JAX-WS", new JaxWSSourcesHandler(this));
this.availableHandlers.put("JAX-RS", new JaxRSSourcesHandler(this));
MavenDeliverableInformation mavenDeliverable = new MavenDeliverableInformation(
this.getSubproject(), project.getGroupId() + ":" + project.getArtifactId());
mavenDeliverable.setTitle(project.getName());
String commit;
if (buildNumber == null || buildNumber.length() < 10) {
commit = "UNKNOWN (ERROR GETTING COMMIT)"; // BUG Talend's crozas
} else {
commit = buildNumber.substring(0, 10);
}
mavenDeliverable.setVersion(project.getVersion() + " (commit " + commit + ")");
if (application != null && !application.trim().isEmpty()) {
mavenDeliverable.setApplication(application);
}
try {
mavenDeliverable.setProperty(Deliverable.XPATH_LOCATION, project.getBasedir().toURI().toURL().toString());
} catch (MalformedURLException e) {
log.error("Failed to convert project location to URL", e);
}
try {
// List dependencies
List<?> dependencies = project.getDependencies();
List<String> formattedDependencies = new ArrayList<String>();
for (Object objectDependency : dependencies) {
Dependency d = (Dependency) objectDependency;
formattedDependencies.add(d.getGroupId() + ":" + d.getArtifactId() + ":" + d.getVersion());
}
mavenDeliverable.setProperty(Deliverable.XPATH_DEPENDENCIES, (Serializable) formattedDependencies);
} catch (MalformedURLException e) {
log.error("Failed to convert project location to URL", e);
}
List<SoaNodeInformation> discoveredNodes = new ArrayList<SoaNodeInformation>();
discoveredNodes.add(mavenDeliverable);
// Configure parser
JavaDocBuilder builder = new JavaDocBuilder();
builder.addSourceTree(project.getBasedir());
// Iterate through classes to find WSes
JavaSource[] sources = builder.getSources();
CodeDiscoveryRegistryClient registryClient = new CodeDiscoveryRegistryClient(registryApi);
for (SourcesHandler handler : availableHandlers.values()) {
discoveredNodes.addAll(handler.handleSources(sources, mavenDeliverable, registryClient, log));
}
// Build and send discovery request
try {
log.info("Sending found SoaNodes to " + nuxeoSitesUrl);
if (discoveredNodes.size() > 0) {
for (SoaNodeInformation soaNode : discoveredNodes) {
log.info("> " + soaNode.getSoaNodeId().toString());
OperationResult result = registryApi.post(soaNode);
if (!result.isSuccessful()) {
IOException ioException = new IOException(result.getMessage());
ioException.setStackTrace(result.getStacktrace());
throw ioException;
}
}
}
} catch (IOException e) {
log.error("Failed to send discovery request", e);
}
}
catch (ClientHandlerException e) {
log.error("Failed to connect to Nuxeo", e);
}
catch (UniformInterfaceException e) {
log.error("Failed to connect to Nuxeo", e);
}
catch (Exception e) {
throw new MojoExecutionException("Failed to discover SOA documents in code", e);
}
}
public boolean isMatchInterfacesFirst() {
return matchInterfacesFirst;
}
public boolean isDiscoverInterfaces() {
return discoverInterfaces;
}
public boolean isDiscoverImplementations() {
return discoverImplementations;
}
public MavenProject getMavenProject() {
return project;
}
public boolean shouldLookupInterfaces(Artifact dependency) {
if (this.interfaceLookupMavenIdRegexPattern == null) {
return true;
}
return this.interfaceLookupMavenIdRegexPattern.matcher(dependency.getId()).matches();
}
private MavenProject getTopLevelMavenProject(MavenProject mavenProject) {
MavenProject parentMavenProject = mavenProject.getParent();
while (parentMavenProject != null && parentMavenProject.getModules().contains(mavenProject.getArtifactId())) { // ex.
// "axxx-dps-apv-core"
mavenProject = parentMavenProject;
parentMavenProject = mavenProject.getParent();
}
return mavenProject;
}
// protected setters are only for testing purpose
public MavenProject getProject() {
return project;
}
protected void setProject(MavenProject project) {
this.project = project;
}
public String getNuxeoSitesUrl() {
return nuxeoSitesUrl;
}
protected void setNuxeoSitesUrl(String nuxeoSitesUrl) {
this.nuxeoSitesUrl = nuxeoSitesUrl;
}
public String getUsername() {
return username;
}
protected void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
protected void setPassword(String password) {
this.password = password;
}
public String getSubproject() {
return subproject;
}
public void setSubproject(String subproject) {
this.subproject = subproject;
}
public String getApplication() {
return application;
}
protected void setApplication(String application) {
this.application = application;
}
public String getBuildNumber() {
return buildNumber;
}
protected void setBuildNumber(String buildNumber) {
this.buildNumber = buildNumber;
}
public String getInterfaceLookupMavenIdRegex() {
return interfaceLookupMavenIdRegex;
}
protected void setInterfaceLookupMavenIdRegex(String interfaceLookupMavenIdRegex) {
this.interfaceLookupMavenIdRegex = interfaceLookupMavenIdRegex;
}
protected void setMatchInterfacesFirst(boolean matchInterfacesFirst) {
this.matchInterfacesFirst = matchInterfacesFirst;
}
protected void setDiscoverInterfaces(boolean discoverInterfaces) {
this.discoverInterfaces = discoverInterfaces;
}
protected void setDiscoverImplementations(boolean discoverImplementations) {
this.discoverImplementations = discoverImplementations;
}
}