Skip to content
Open
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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ It is used in combination with the [artifact-version-service](https://github.com
<plugin>
<groupId>de.westemeyer</groupId>
<artifactId>artifact-version-maven-plugin</artifactId>
<version>2.0.1</version>
<version>2.1.0</version>
<executions>
<execution>
<goals>
Expand Down Expand Up @@ -98,7 +98,7 @@ It is also possible to configure the generator to use target directories and a m
<plugin>
<groupId>de.westemeyer</groupId>
<artifactId>artifact-version-maven-plugin</artifactId>
<version>2.0.1</version>
<version>2.1.0</version>
<executions>
<execution>
<goals>
Expand All @@ -113,6 +113,8 @@ It is also possible to configure the generator to use target directories and a m
<targetFolder>target/generated-sources</targetFolder>
<!-- or use NATIVE for plain Java services, SPRING_BOOT is the default value -->
<serviceType>SPRING_BOOT</serviceType>
<!-- optionally control @NullMarked generation (auto-detected if org.jspecify:jspecify dependency is present) -->
<addNullMarked>true</addNullMarked>
</configuration>
</plugin>
</plugins>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>de.westemeyer</groupId>
<artifactId>artifact-version-maven-plugin</artifactId>
<version>2.0.1</version>
<version>2.1.0</version>

<name>Maven source code generator for artifact version services.</name>
<description>The artifact-version-maven-plugin is used to automatically generate artifact version information to be collected by ArtifactVersionCollector somewhere in the classpath.</description>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.westemeyer.plugins.maven.versions;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
Expand Down Expand Up @@ -51,6 +53,16 @@ public class GenerateServiceMojo extends AbstractMojo {
*/
private static final String AUTO_CONFIGURATION_STRING = "AutoConfiguration";

/**
* Group ID for JSpecify.
*/
private static final String JSPECIFY_GROUP_ID = "org.jspecify";

/**
* Artifact ID for JSpecify.
*/
private static final String JSPECIFY_ARTIFACT_ID = "jspecify";

/**
* The project object is injected with information from a project's pom.xml.
*/
Expand Down Expand Up @@ -95,6 +107,13 @@ public class GenerateServiceMojo extends AbstractMojo {
@Parameter
boolean skipSpringBootAutoConfiguration = false;

/**
* Whether to annotate generated classes with org.jspecify.annotations.NullMarked.
* Defaults to true if org.jspecify:jspecify is present in project dependencies.
*/
@Parameter
Boolean addNullMarked;

@Override
public void execute() throws MojoFailureException {
String packaging = project.getPackaging();
Expand Down Expand Up @@ -338,9 +357,61 @@ Map<String, String> getTemplateValues(String autoconfigurationClass) {
valueMap.put("description", replaceLineFeeds(project.getDescription()));
valueMap.put("timestamp", "" + new Date().getTime());
valueMap.put("parentArtifactDefinition", getParentArtifactDefinition());
boolean useNullMarked = isNullMarked();
valueMap.put("nullMarkedImport", useNullMarked ? "import org.jspecify.annotations.NullMarked;\n" : "");
valueMap.put("nullMarkedAnnotation", useNullMarked ? "@NullMarked\n" : "");
return valueMap;
}

/**
* Determine whether generated classes should be annotated with @NullMarked.
*
* @return true if addNullMarked is explicitly true, or if addNullMarked is not set and jspecify is present
*/
boolean isNullMarked() {
if (addNullMarked != null) {
return addNullMarked;
}
return isJSpecifyPresent();
}

/**
* Check whether org.jspecify:jspecify dependency is present in the project dependencies or artifacts.
*
* @return true if org.jspecify:jspecify is found
*/
boolean isJSpecifyPresent() {
for (Object depObj : project.getDependencies()) {
if (depObj instanceof Dependency) {
Dependency dependency = (Dependency) depObj;
if (isJspecify(dependency.getGroupId(), dependency.getArtifactId())) {
return true;
}
}
}
for (Object artObj : project.getArtifacts()) {
if (artObj instanceof Artifact) {
Artifact artifact = (Artifact) artObj;
if (isJspecify(artifact.getGroupId(), artifact.getArtifactId())) {
return true;
}
}
}
return false;
}

/**
* Determine, whether given coordinates are specify.
*
* @param groupId the object's group ID
* @param artifactId the object's artifact ID
* @return whether the given group ID and artifact ID match jspecify
*/
private static boolean isJspecify(String groupId, String artifactId) {
return JSPECIFY_GROUP_ID.equals(groupId)
&& JSPECIFY_ARTIFACT_ID.equals(artifactId);
}

/**
* Determine the content for the creation of parent artifacts.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package ${package};

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

${nullMarkedImport}
/**
* Configuration file used to automatically add the {@link de.westemeyer.version.service.ArtifactVersionCollector} bean.
*/
@Configuration
${nullMarkedAnnotation}@Configuration
@ComponentScan
public class ${configClass} {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import de.westemeyer.version.core.api.ArtifactVersionService;
import de.westemeyer.version.core.model.Artifact;
import de.westemeyer.version.core.model.BasicArtifact;
import org.springframework.stereotype.Service;

${nullMarkedImport}
/**
* Generated version service implementation class for ${groupId}:${artifactId} artifact.
*/
@Service
public class ${serviceClass} implements ArtifactVersionService {
${nullMarkedAnnotation}public class ${serviceClass} implements ArtifactVersionService {
@Override
public Artifact getArtifact() {
${parentArtifactDefinition}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package ${package};
import de.westemeyer.version.core.api.ArtifactVersionService;
import de.westemeyer.version.core.model.Artifact;
import org.springframework.stereotype.Service;

${nullMarkedImport}
/**
* Generated version service implementation class for ${groupId}:${artifactId} artifact.
*/
@Service
public class ${serviceClass} implements ArtifactVersionService {
${nullMarkedAnnotation}public class ${serviceClass} implements ArtifactVersionService {
@Override
public Artifact getArtifact() {
return new Artifact("${groupId}", "${artifactId}", "${version}", ${timestamp}L, "${name}", "${description}", "${url}", null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package ${package};
import de.westemeyer.version.core.api.ArtifactVersionService;
import de.westemeyer.version.core.model.Artifact;
import de.westemeyer.version.core.model.BasicArtifact;

${nullMarkedImport}
/**
* Generated version service implementation class for ${groupId}:${artifactId} artifact.
*/
public class ${serviceClass} implements ArtifactVersionService {
${nullMarkedAnnotation}public class ${serviceClass} implements ArtifactVersionService {
@Override
public Artifact getArtifact() {
${parentArtifactDefinition}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ package ${package};

import de.westemeyer.version.core.api.ArtifactVersionService;
import de.westemeyer.version.core.model.Artifact;

${nullMarkedImport}
/**
* Generated version service implementation class for ${groupId}:${artifactId} artifact.
*/
public class ${serviceClass} implements ArtifactVersionService {
${nullMarkedAnnotation}public class ${serviceClass} implements ArtifactVersionService {
@Override
public Artifact getArtifact() {
return new Artifact("${groupId}", "${artifactId}", "${version}", ${timestamp}L, "${name}", "${description}", "${url}", null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package de.westemeyer.plugins.maven.versions;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
Expand All @@ -22,8 +24,11 @@
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.InvalidPathException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
Expand Down Expand Up @@ -115,9 +120,96 @@ void getTemplateValues() {
assertEquals(
" BasicArtifact parentArtifact = new BasicArtifact(\"de.westemeyer.parent\", \"artifact-version-test-parent\", \"1.0.0\", null);\n",
templateValues.get("parentArtifactDefinition"));
assertEquals("", templateValues.get("nullMarkedImport"));
assertEquals("", templateValues.get("nullMarkedAnnotation"));
assertDoesNotThrow(() -> Long.valueOf(templateValues.get("timestamp")));
}

@Test
void getTemplateValuesWithNullMarked() {
// given
GenerateServiceMojo mojo = getServiceMojoMock();
mojo.serviceClass = "MyServiceClass";
mojo.packageName = "de.westemeyer.service.version";
mojo.addNullMarked = true;
mojo.project = getMavenProject("de.westemeyer", "artifact-version-test", "1.0.0-SNAPSHOT");
when(mojo.getTemplateValues(anyString())).thenCallRealMethod();
when(mojo.getParentArtifactDefinition()).thenCallRealMethod();
when(mojo.isNullMarked()).thenCallRealMethod();

// when
Map<String, String> templateValues = mojo.getTemplateValues("ConfigClass");

// then
assertEquals("import org.jspecify.annotations.NullMarked;\n", templateValues.get("nullMarkedImport"));
assertEquals("@NullMarked\n", templateValues.get("nullMarkedAnnotation"));
}

@Test
void isNullMarked() {
// given
GenerateServiceMojo mojo = getServiceMojoMock();
when(mojo.isNullMarked()).thenCallRealMethod();
when(mojo.isJSpecifyPresent()).thenReturn(true);

// addNullMarked is null
assertTrue(mojo.isNullMarked());

// explicit true
mojo.addNullMarked = true;
assertTrue(mojo.isNullMarked());

// explicit false
mojo.addNullMarked = false;
assertFalse(mojo.isNullMarked());
}

@ParameterizedTest(name = "{0}")
@CsvSource({"Dependency found,org.jspecify,jspecify,true", "Other group ID,org.junspecify,jspecify,false", "Other group ID,org.jspecify,junspecify,false"})
void isJSpecifyPresentDirectDependency(String name, String groupId, String artifactId, boolean expected) {
// given
GenerateServiceMojo mojo = getServiceMojoMock();
when(mojo.isJSpecifyPresent()).thenCallRealMethod();
Dependency dependency = new Dependency();
dependency.setGroupId(groupId);
dependency.setArtifactId(artifactId);
List<Object> dependencies = new ArrayList<>();
dependencies.add(new Object());
dependencies.add(dependency);
when(mojo.project.getDependencies()).thenReturn(dependencies);

// when/then
assertEquals(expected, mojo.isJSpecifyPresent());
}

@ParameterizedTest(name = "{0}")
@CsvSource({"Dependency found,org.jspecify,jspecify,true", "Other group ID,org.junspecify,jspecify,false", "Other group ID,org.jspecify,junspecify,false"})
void isJSpecifyPresentArtifact(String name, String groupId, String artifactId, boolean expected) {
// given
GenerateServiceMojo mojo = getServiceMojoMock();
when(mojo.isJSpecifyPresent()).thenCallRealMethod();
Artifact artifact = mock(Artifact.class);
when(artifact.getGroupId()).thenReturn(groupId);
when(artifact.getArtifactId()).thenReturn(artifactId);
List<Object> artifacts = new ArrayList<>();
artifacts.add(new Object());
artifacts.add(artifact);
when(mojo.project.getArtifacts()).thenReturn(new CopyOnWriteArraySet<>(artifacts));

// when/then
assertEquals(expected, mojo.isJSpecifyPresent());
}

@Test
void isJSpecifyPresentNotFound() {
// given
GenerateServiceMojo mojo = getServiceMojoMock();
when(mojo.isJSpecifyPresent()).thenCallRealMethod();

// when/then
assertFalse(mojo.isJSpecifyPresent());
}

private static MavenProject getMavenProject(String groupId, String artifactId, String version) {
MavenProject project = mock(MavenProject.class);
when(project.getGroupId()).thenReturn(groupId);
Expand Down