Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #367 #372

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ workspace "aadarchi-documentation-system" {
"aadarchi.maven.pom" "pom.xml"
}
maven = container "maven" "The maven build engine" "java, maven"
aadarchi_maven_plugin = container "aadarchi-maven-plugin" "" "java, maven-plugin"{
aadarchi_maven_plugin = container "aadarchi-maven-plugin" {
properties {
"aadarchi.sequence.generator.with" "true"
}
maven -> this "Invokes this plugin during build to generate data"
}
aadarchi_base = container "base" "" "Java, CDI" {
aadarchi_base = container "base"{
properties {
"aadarchi.sequence.generator.with" "true"
}
Expand All @@ -24,7 +24,7 @@ workspace "aadarchi-documentation-system" {
aadarchi_maven_plugin -> this
}
archetype_6 = container "archetype" "" "maven"
architecture_documentation = container "architecture-documentation" "" "java, maven, structurizr, asciidoc"
architecture_documentation = container "architecture-documentation"
}
person_architect -> archetype_6 "Bootstrap a valid project"
person_architect -> maven "Generates documentation"
Expand Down
12 changes: 6 additions & 6 deletions maven-metadata-inferer/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230618</version>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
</dependencies>
<build>
Expand All @@ -66,8 +65,7 @@
<id>Download popular artifacts</id>
<activation>
<file>
<missing>
${project.build.directory}/generated/resources/mvnrepository/artifacts.json</missing>
<missing><![CDATA[${project.build.directory}/generated/resources/mvnrepository/artifacts.json]]></missing>
</file>
</activation>
<build>
Expand All @@ -87,6 +85,8 @@
good -->
<url><![CDATA[https://raw.githubusercontent.com/Riduidel/aadarchi-technology-detector/reports/mvnrepository/artifacts.json]]></url>
<unpack>false</unpack>
<overwrite>true</overwrite>
<skipCache>true</skipCache>
<outputDirectory><![CDATA[${project.build.directory}/generated/resources]]></outputDirectory>
<outputFileName>mvnrepository.json</outputFileName>
</configuration>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package org.ndx.aadarchi.inferer.maven;

import java.util.Map;

import org.ndx.aadarchi.base.enhancers.ModelElementKeys;

import com.fasterxml.jackson.core.type.TypeReference;

public interface MavenEnhancer {
/**
* URL of the maven pom the model element represents.
Expand Down Expand Up @@ -29,4 +33,7 @@ public interface MavenEnhancer {
* When set, this allows users to enter a list of profiles names separated by ";"
*/
String AGILE_ARCHITECTURE_MAVEN_ADDITIONAL_PROFILES = ModelElementKeys.PREFIX+"maven.profiles";

String AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES = ModelElementKeys.PREFIX+"maven.technologies";
TypeReference<Map<String, String>> AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES_TYPE = new TypeReference<Map<String, String>>() {};
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package org.ndx.aadarchi.inferer.maven;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiFunction;
import java.util.logging.Logger;
import java.util.stream.Collectors;
Expand All @@ -17,22 +13,18 @@
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Default;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.IssueManagement;
import org.apache.maven.model.Scm;
import org.apache.maven.project.MavenProject;
import org.json.JSONObject;
import org.ndx.aadarchi.base.enhancers.ModelElementKeys;
import org.ndx.aadarchi.inferer.maven.technologies.TechnologyDecorator;

import com.pivovarit.function.ThrowingFunction;
import com.pivovarit.function.ThrowingPredicate;
import com.structurizr.model.Component;
import com.structurizr.model.Container;
import com.structurizr.model.Element;

@Default
Expand All @@ -42,7 +34,7 @@ public class MavenPomDecorator {
@Inject
FileSystemManager fileSystemManager;

@Inject @Named(MvnRepositoryArtifactsProducer.MVNREPOSITORY_ARTIFACTS) JSONObject mvnRepositoryArtifacts;
@Inject TechnologyDecorator technologyDecorator;

public static void decorateRecursively(MavenProject project, BiFunction<MavenProject, List<MavenProject>, Boolean> consumer) {
decorateRecursively(project, new LinkedList<MavenProject>(), consumer);
Expand Down Expand Up @@ -72,7 +64,7 @@ public void decorate(Element element, MavenProject mavenProject) {
decorateJavaSource(element, mavenProject);
decorateJavaPackage(element, mavenProject);
decorateMavenProperties(element, mavenProject);
decorateTechnology(element, mavenProject);
technologyDecorator.decorateTechnology(element, mavenProject);
Optional.ofNullable(mavenProject.getDescription()).stream()
.forEach(description -> element.setDescription(description.replaceAll("\n", " ")));
}
Expand Down Expand Up @@ -177,98 +169,4 @@ private String findFirstPackageLevelInPath(FileObject root, FileObject current)
.toString()
.replace('/', '.').replace('\\', '.');
}

protected static String technologyWithVersionFromProperty(MavenProject mavenProject, String technology, String... propertyNames) {
return technology + Stream.of(propertyNames)
.flatMap(p -> new HashSet(Arrays.asList(p, p.replace('.', '-'), p.replace('-', '.'))).stream())
.filter(p -> mavenProject.getProperties().containsKey(p))
.map(p -> mavenProject.getProperties().get(p))
.map(text -> " "+text)
.findFirst()
.orElse("")
;
}

/**
* TODO replace that with usage of {@link #mvnRepositoryArtifacts}
* @param project
* @return
*/
public static Set<String> doDecorateTechnology(MavenProject project) {
Set<String> technologies = new LinkedHashSet<String>();
switch (project.getPackaging()) {
case "ear":
technologies.add("Java");
technologies.add("ear");
break;
case "war":
technologies.add("Java");
technologies.add("war");
case "jar":
// If there is a java version property, use it to detect Java version
technologies.add(MavenPomDecorator.technologyWithVersionFromProperty(project, "Java", "java.version", "maven.compiler.target"));
break;
case "pom":
break;
default:
logger.warning(String.format(
"Maven component %s uses packaging %s which we don't know. Please submit a bug to aadarchi-documentation-system to have this particular packaging correctly handled",
project, project.getPackaging()));
}
for (Dependency dependency : (List<Dependency>) project.getDependencies()) {
switch (dependency.getGroupId()) {
case "org.apache.maven":
if("maven-plugin-api".equals(dependency.getArtifactId())) {
technologies.add("maven-plugin");
}
break;
case "io.quarkus":
technologies.add(MavenPomDecorator.technologyWithVersionFromProperty(project, "Quarkus", "quarkus.platform.version"));
break;
case "org.springframework":
technologies.add("Spring");
break;
case "org.apache.camel":
technologies.add(MavenPomDecorator.technologyWithVersionFromProperty(project, "Apache Camel", "camel.version"));
break;
case "org.springframework.boot":
technologies.add("Spring Boot");
break;
case "com.google.gwt":
technologies.add("GWT");
break;
case "javax.enterprise":
if("cdi-api".equals(dependency.getArtifactId())) {
technologies.add("CDI");
}
break;
}
}
return technologies;
}

/**
* @param element element for which we want the technologies
* @param project
* @return a string giving details about important project infos
*/
public void decorateTechnology(Element element, MavenProject project) {
// TODO replace simple set with something more complex
// Maybe a set of artifact objects
Set<String> technologies = new TreeSet<String>();
decorateRecursively(project, (p, l) -> {
technologies.addAll(MavenPomDecorator.doDecorateTechnology(p));
// We should explore all parent poms
return true;
});
String technologiesText = technologies.stream().collect(Collectors.joining(","));
if(element instanceof Component) {
((Component) element).setTechnology(technologiesText);
} else if(element instanceof Container) {
((Container) element).setTechnology(technologiesText);
}
// TODO add a property to element containing the technologies artifact ids
// For later being able to give some details
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package org.ndx.aadarchi.inferer.maven;

import java.util.Map;
import java.util.stream.Collectors;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Default;
import javax.inject.Inject;
import javax.inject.Named;

import org.ndx.aadarchi.base.AgileArchitectureSection;
import org.ndx.aadarchi.base.OutputBuilder;
import org.ndx.aadarchi.base.OutputBuilder.HandledFormat;
import org.ndx.aadarchi.base.enhancers.ModelElementAdapter;
import org.ndx.aadarchi.inferer.maven.technologies.MvnRepositoryArtifact;
import org.ndx.aadarchi.inferer.maven.technologies.MvnRepositoryArtifactsProducer;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.structurizr.model.StaticStructureElement;

/**
* An enhancer which generates in development environment some text about the
* notable dependencies
*/
@Default @ApplicationScoped
public class MavenTechnologiesDocumentationEnhancer extends ModelElementAdapter {
@Inject @Named(MvnRepositoryArtifactsProducer.MVNREPOSITORY_ARTIFACTS) Map<String, MvnRepositoryArtifact> mvnRepositoryArtifacts;
ObjectMapper objectMapper = new ObjectMapper();

@Override
public int priority() {
return 10;
}

@Override
protected void processElement(StaticStructureElement element, OutputBuilder builder) {
if(element.getProperties().containsKey(MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES)) {
String technologies = element.getProperties().get(MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES);
// Rehydrate that to have artifacts
try {
Map<String, String> dependenciesVersions = objectMapper.readValue(technologies, MavenEnhancer.AGILE_ARCHITECTURE_MAVEN_TECHNOLOGIES_TYPE);
if(!dependenciesVersions.isEmpty()) {
writeDependenciesArtifacts(element, dependenciesVersions, builder);
}
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

private void writeDependenciesArtifacts(StaticStructureElement element, Map<String, String> dependenciesVersions,
OutputBuilder builder) {
String text = dependenciesVersions.entrySet().stream()
.map(entry -> Map.entry(mvnRepositoryArtifacts.get(entry.getKey()), entry.getValue()))
.map(entry -> this.toTableRow(entry.getKey(), entry.getValue()))
.collect(Collectors.joining("\n",
"[%autowidth.stretch, cols=\"1a,1a,1a,1a\"]\n|==="
+ "\n|Name|Used version|Categories|Description\n\n",
"\n|==="));
builder.writeToOutput(AgileArchitectureSection.development_environment, element, this, OutputBuilder.Format.adoc,
"# Interesting dependencies\n\n" +
text);
}

private String toTableRow(MvnRepositoryArtifact key, String version) {
StringBuilder returned = new StringBuilder()
.append("|").append(key.page).append("[").append(key.name).append("] ");
returned
.append("|").append(version==null || version.isBlank() ? "{nbsp}" : version);
if(key.versions.containsKey(version))
returned.append(" (released ").append(key.versions.get(version)).append(")");
returned
.append("|").append(key.categories.isEmpty() ? "{nbsp}" : key.categories.stream().collect(Collectors.joining()))
.append("|").append(key.description)
;
return returned.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.ndx.aadarchi.inferer.maven.technologies;

import java.util.List;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class MvnRepositoryArtifact {
public final String name;
public final String coordinates;
public String description;
public List<String> categories;
public List<String> tags;
public Map<String, String> versions;
public String page;

@JsonCreator
public MvnRepositoryArtifact(@JsonProperty("name") String name,
@JsonProperty("coordinates") String coordinates,
@JsonProperty("description") String description,
@JsonProperty("page") String page,
@JsonProperty("categories") List<String> categories,
@JsonProperty("tags") List<String> tags,
@JsonProperty("versions") Map<String, String> versions
) {
super();
this.name = name;
this.coordinates = coordinates;
this.page = page;
this.description = description;
this.categories = categories;
this.tags = tags;
this.versions = versions;
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
package org.ndx.aadarchi.inferer.maven;
package org.ndx.aadarchi.inferer.maven.technologies;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.logging.Logger;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Named;

import org.apache.commons.io.IOUtils;
import org.json.JSONObject;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

public class MvnRepositoryArtifactsProducer {
private static final Logger logger = Logger.getLogger(MvnRepositoryArtifactsProducer.class.getName());
public static final String MVNREPOSITORY_ARTIFACTS = "mvnRepositoryArtifactsMap";

/**
* @see https://stleary.github.io/JSON-java/index.html
* @return a JSONObject containing all popular artifacts
* @throws IOException
*/
@Produces @ApplicationScoped @Named(MVNREPOSITORY_ARTIFACTS) JSONObject createMvnRepositoryArtifacts() throws IOException {
@Produces @ApplicationScoped @Named(MVNREPOSITORY_ARTIFACTS) Map<String, MvnRepositoryArtifact> createMvnRepositoryArtifacts() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
logger.info(
String.format("Loading mvnrepository popular artifacts from %s",
getClass().getClassLoader().getResource("mvnrepository.json")));
try(InputStream input = getClass().getClassLoader().getResourceAsStream("mvnrepository.json")) {
String text = IOUtils.toString(input, "UTF-8");
return new JSONObject(text);
return objectMapper.readValue(input, new TypeReference<Map<String, MvnRepositoryArtifact>>() {});
}
}
}
Loading
Loading