This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-slingfeature-maven-plugin.git
The following commit(s) were added to refs/heads/master by this push:
new 58d511d SLING-7961 : Adjust feature file reading to latest state
58d511d is described below
commit 58d511d4a0987f69ba7f5fbd060de0272ab804ec
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Fri Sep 28 16:34:20 2018 +0200
SLING-7961 : Adjust feature file reading to latest state
---
README.md | 27 ++-
.../apache/sling/feature/maven/Preprocessor.java | 3 +
.../feature/maven/mojos/AggregateFeatures.java | 213 ++++++++++-----------
.../feature/maven/mojos/AggregateFeaturesTest.java | 41 ++--
4 files changed, 143 insertions(+), 141 deletions(-)
diff --git a/README.md b/README.md
index 96b3318..ef29a2b 100644
--- a/README.md
+++ b/README.md
@@ -8,13 +8,15 @@ This module is part of the [Apache
Sling](https://sling.apache.org) project.
Maven Plugin for OSGi Applications
-## Supported goals
+# Global Configuration
+
+* features : The directory containing the feature files. The default is
`src/main/features` and all files ending with `.json` are read including all
sub directories.
-### generate-resources
-This goal processed feature files to substitute Maven variable placeholders in
feature files such as `${project.groupId}`, `${project.artifactId}` and
`${project.version}`
+## Supported goals
### aggregate-features
-Produce an aggregated feature from a list of features.
+
+Produce an aggregated feature from a list of features. The list of features is
either specified by include/exclude patterns based on the configured features
directory of the project or Maven coordinates of features.
Sample configuration:
@@ -26,15 +28,13 @@ Sample configuration:
<executions>
<execution>
<id>merge-features</id>
- <phase>generate-resources</phase>
<goals>
<goal>aggregate-features</goal>
</goals>
<configuration>
- <classifier>my-aggregated-feature</classifier>
+ <aggregateClassifier>my-aggregated-feature</aggregateClassifier>
<aggregates>
<directory>
- <location>${basedir}/src/main/my-features</location>
<includes>*.json</includes>
<excludes>exclude-me.json</excludes>
<excludes>exclude-me-too.json</excludes>
@@ -62,13 +62,11 @@ Sample configuration:
</plugin>
```
-All features found in both the directories as well as the artifact sections of
the configuration are aggregated into a single feature.
+All features found in the directory as well as the artifact sections of the
configuration are aggregated into a single feature.
-The merged feature will have the same `groupId`, `artifactId` and `version` as
the pom in which
-the aggregation is configured. It will have type `slingfeature` and as
classifier the one specified
-in the configuration.
+The merged feature will have the same `groupId`, `artifactId` and `version` as
the pom in which the aggregation is configured. It will have type
`slingfeature` and as classifier the one specified in the configuration named
`aggregateClassifier`.
-Variables and framework properties can be overridden using the `<variables>`
and
+Variables and framework properties can be overridden using the `<variables>`
and
`<fraweworkProperties>` sections. If multiple definitions of the same
variables are found
in the feature that are to be aggregated and the values for these variables
are different,
they *must* be overridden, otherwise the aggregation will generate an error.
@@ -78,7 +76,7 @@ they *must* be overridden, otherwise the aggregation will
generate an error.
Merging of extensions is specific to the extension being merged. Handlers can
be provided to implement the logic of extension merging. A handler needs to
implement the `org.apache.sling.feature.builder.FeatureExtensionHandler` and is
looked up via the Java ServiceLoader mechanism.
-To provide additional handlers to the `slingfeature-maven-plugin`, list the
artifacts in the `<dependencies>`
+To provide additional handlers to the `slingfeature-maven-plugin`, list the
artifacts in the `<dependencies>`
section of the plugin configuration:
```
@@ -100,6 +98,5 @@ section of the plugin configuration:
```
### attach-features
-Attach feature files found in the project to the projects produced artifacts.
This includes features
+Attach feature files found in the project to the projects produced artifacts.
This includes features
found in `src/main/features` as well as features produce with the
`aggregate-features` goal.
-
diff --git a/src/main/java/org/apache/sling/feature/maven/Preprocessor.java
b/src/main/java/org/apache/sling/feature/maven/Preprocessor.java
index 249b690..9ae7a49 100644
--- a/src/main/java/org/apache/sling/feature/maven/Preprocessor.java
+++ b/src/main/java/org/apache/sling/feature/maven/Preprocessor.java
@@ -43,6 +43,9 @@ import org.codehaus.plexus.logging.Logger;
/**
* The processor processes all feature projects.
+ *
+ * TODO - we should check that only one feature has no classifier
+ * and that the classifiers are unique within the read set
*/
public class Preprocessor {
diff --git
a/src/main/java/org/apache/sling/feature/maven/mojos/AggregateFeatures.java
b/src/main/java/org/apache/sling/feature/maven/mojos/AggregateFeatures.java
index ee5fe32..b66e759 100644
--- a/src/main/java/org/apache/sling/feature/maven/mojos/AggregateFeatures.java
+++ b/src/main/java/org/apache/sling/feature/maven/mojos/AggregateFeatures.java
@@ -25,13 +25,13 @@ import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Spliterator;
import java.util.Spliterators;
+import java.util.StringTokenizer;
import java.util.stream.StreamSupport;
import org.apache.maven.artifact.Artifact;
@@ -58,9 +58,13 @@ import org.apache.sling.feature.io.json.FeatureJSONWriter;
import org.apache.sling.feature.maven.FeatureConstants;
import org.apache.sling.feature.maven.ProjectHelper;
import org.apache.sling.feature.maven.Substitution;
+import org.codehaus.plexus.util.AbstractScanner;
/**
* Aggregate multiple features into a single one.
+ *
+ * TODO - check that classifier is not clashing with an already used
classifier from the read
+ * files. We should also check if this mojo is configured several times, that
different classifiers are configured.
*/
@Mojo(name = "aggregate-features",
defaultPhase = LifecyclePhase.PACKAGE,
@@ -73,7 +77,7 @@ public class AggregateFeatures extends AbstractFeatureMojo {
List<FeatureConfig> aggregates;
@Parameter(required = true)
- String classifier;
+ String aggregateClassifier;
@Parameter(required = false)
Map<String,String> variables;
@@ -95,13 +99,16 @@ public class AggregateFeatures extends AbstractFeatureMojo {
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
+ // get map of all project features
final Map<String, Feature> projectFeatures =
ProjectHelper.getFeatures(this.project);
- Map<ArtifactId, Feature> contextFeatures = new HashMap<>();
+ // ..and hash them by artifact id
+ final Map<ArtifactId, Feature> contextFeatures = new HashMap<>();
for(final Map.Entry<String, Feature> entry :
projectFeatures.entrySet()) {
contextFeatures.put(entry.getValue().getId(), entry.getValue());
}
- Map<ArtifactId, Feature> featureMap = readFeatures(aggregates,
projectFeatures);
+ // get the map of features for this aggregate
+ final Map<ArtifactId, Feature> featureMap = readFeatures(aggregates,
projectFeatures);
KeyValueMap variableOverrides = new KeyValueMap();
if (variables != null) {
@@ -135,11 +142,11 @@ public class AggregateFeatures extends
AbstractFeatureMojo {
.toArray(FeatureExtensionHandler[]::new));
ArtifactId newFeatureID = new ArtifactId(project.getGroupId(),
project.getArtifactId(),
- project.getVersion(), classifier,
FeatureConstants.PACKAGING_FEATURE);
+ project.getVersion(), aggregateClassifier,
FeatureConstants.PACKAGING_FEATURE);
Feature result = FeatureBuilder.assemble(newFeatureID, builderContext,
featureMap.values().toArray(new Feature[] {}));
// write the feature
- final File outputFile = new
File(this.project.getBuild().getDirectory() + File.separatorChar + classifier +
".json");
+ final File outputFile = new
File(this.project.getBuild().getDirectory() + File.separatorChar +
aggregateClassifier + ".json");
outputFile.getParentFile().mkdirs();
try ( final Writer writer = new FileWriter(outputFile)) {
@@ -150,19 +157,22 @@ public class AggregateFeatures extends
AbstractFeatureMojo {
// attach it as an additional artifact
projectHelper.attachArtifact(project,
FeatureConstants.PACKAGING_FEATURE,
- classifier, outputFile);
+ aggregateClassifier, outputFile);
}
private Map<ArtifactId, Feature> readFeatures(Collection<FeatureConfig>
featureConfigs,
- Map<String, Feature> contextFeatures) throws
MojoExecutionException {
- Map<ArtifactId, Feature> featureMap = new LinkedHashMap<>();
+ Map<String, Feature> contextFeatures)
+ throws MojoExecutionException {
+ final Map<ArtifactId, Feature> featureMap = new LinkedHashMap<>();
try {
- for (FeatureConfig fc : featureConfigs) {
- if (fc.location != null) {
+ for (final FeatureConfig fc : featureConfigs) {
+ if (fc.isDirectory()) {
readFeaturesFromDirectory(fc, featureMap, contextFeatures);
- } else if (fc.artifactId != null) {
+ } else if (fc.isArtifact()) {
readFeatureFromMavenArtifact(fc, featureMap,
contextFeatures);
+ } else {
+ throw new MojoExecutionException("Invalid aggregate
configuration: " + fc);
}
}
} catch (IOException e) {
@@ -172,6 +182,7 @@ public class AggregateFeatures extends AbstractFeatureMojo {
return featureMap;
}
+ // TODO this only works for projects not being part of the reactor build
private void readFeatureFromMavenArtifact(FeatureConfig fc,
Map<ArtifactId, Feature> featureMap,
Map<String, Feature> contextFeatures) throws IOException {
ArtifactId id = new ArtifactId(fc.groupId, fc.artifactId, fc.version,
fc.classifier, fc.type);
@@ -215,100 +226,19 @@ public class AggregateFeatures extends
AbstractFeatureMojo {
return artFile;
}
- private void readFeaturesFromDirectory(FeatureConfig fc, Map<ArtifactId,
Feature> featureMap, Map<String, Feature> contextFeatures) throws IOException {
- if ( fc.location.startsWith("/") || fc.location.startsWith(".") ) {
- throw new IOException("Invalid location: " + fc.location);
- }
-
- final File f = new File(this.project.getBasedir(),
fc.location.replace('/', File.separatorChar).replace('\\', File.separatorChar));
- final String prefix = f.getAbsolutePath().concat(File.separator);
- final Map<String, Feature> candidates = new LinkedHashMap<>();
- for(final Map.Entry<String, Feature> entry :
contextFeatures.entrySet()) {
- if ( entry.getKey().startsWith(prefix) ) {
- candidates.put(entry.getKey(), entry.getValue());
- }
- }
-
- Map<String,String> includes = new HashMap<>();
- Map<String,String> excludes = new HashMap<>();
-
- for (String inc : fc.includes) {
- includes.put(inc, convertGlobToRegex(inc));
- }
- for (String exc : fc.excludes) {
- excludes.put(exc, convertGlobToRegex(exc));
- }
- Map<String,Feature> readFeatures = new HashMap<>();
-
- nextFile:
- for (Map.Entry<String, Feature> entry : candidates.entrySet()) {
- final String fileName = new File(entry.getKey()).getName();
-
- // First check that it is allowed as part of the includes
- boolean matchesIncludes = fc.includes.size() == 0;
-
- for (Iterator<String> it = includes.values().iterator();
it.hasNext(); ) {
- String inc = it.next();
- if (fileName.matches(inc)) {
- matchesIncludes = true;
- if (!isGlob(inc)) {
- // Not a glob
- it.remove();
- }
- break;
- }
- }
-
- if (!matchesIncludes)
- continue nextFile;
-
- // Ensure there is no exclusion for it
- for (Iterator<String> it = excludes.values().iterator();
it.hasNext(); ) {
- String exc = it.next();
- if (fileName.matches(exc)) {
- if (!isGlob(exc)) {
- // Not a glob
- it.remove();
- }
- continue nextFile;
- }
- }
-
- readFeatures.put(entry.getKey(), entry.getValue());
- }
-
- // Ordering:
- // put the read features in the main featureMap, order the non-globbed
ones as specified in the plugin
- for (String inc : fc.includes) {
- Feature feat = readFeatures.remove(inc);
- if (feat != null) {
- featureMap.put(feat.getId(), feat);
- }
- }
- // Put all the remaining features on the map
- readFeatures.values().stream().forEach(v -> featureMap.put(v.getId(),
v));
-
- // If there are any non-glob includes/excludes left, fail as the
plugin is then incorrectly configured
- for (Map.Entry<String,String> i : includes.entrySet()) {
- if (!isGlob(i.getValue())) {
- throw new IOException("Non-wildcard include " + i.getKey() + "
not found.");
- }
+ private void readFeaturesFromDirectory(FeatureConfig fc,
+ Map<ArtifactId, Feature> featureMap,
+ Map<String, Feature> contextFeatures) throws IOException {
+ final FeatureScanner scanner = new FeatureScanner(contextFeatures);
+ if ( fc.includes.isEmpty() ) {
+ scanner.setIncludes(fc.includes.toArray(new
String[fc.includes.size()]));
}
- for (Map.Entry<String,String> e : excludes.entrySet()) {
- if (!isGlob(e.getValue())) {
- throw new IOException("Non-wildcard exclude " + e.getKey() + "
not found.");
- }
+ if ( fc.excludes.isEmpty() ) {
+ scanner.setExcludes(fc.excludes.toArray(new
String[fc.excludes.size()]));
}
- }
+ scanner.scan();
- private boolean isGlob(String name) {
- return name.contains("*");
- }
-
- private String convertGlobToRegex(String glob) {
- glob = glob.replace(".", "[.]");
- glob = glob.replace("*", ".*");
- return glob;
+ featureMap.putAll(scanner.getIncluded());
}
private Feature readFeatureFromFile(File f) throws IOException {
@@ -319,7 +249,6 @@ public class AggregateFeatures extends AbstractFeatureMojo {
public static class FeatureConfig {
// If the configuration is a directory
- String location;
List<String> includes = new ArrayList<>();
List<String> excludes = new ArrayList<>();
@@ -330,8 +259,20 @@ public class AggregateFeatures extends AbstractFeatureMojo
{
String type;
String classifier;
- public void setLocation(String loc) {
- location = loc;
+ public boolean isDirectory() {
+ return (!this.includes.isEmpty() || !this.excludes.isEmpty())
+ && groupId == null
+ && artifactId == null
+ && version == null
+ && type == null
+ && classifier == null;
+ }
+
+ public boolean isArtifact() {
+ return this.includes.isEmpty() && this.excludes.isEmpty()
+ && this.groupId != null
+ && this.artifactId != null
+ && this.version != null;
}
public void setIncludes(String i) {
@@ -364,9 +305,67 @@ public class AggregateFeatures extends AbstractFeatureMojo
{
@Override
public String toString() {
- return "FeatureConfig [location=" + location + ", includes=" +
includes + ", excludes=" + excludes + ", groupId="
+ return "FeatureConfig [includes=" + includes + ", excludes=" +
excludes + ", groupId="
+ groupId + ", artifactId=" + artifactId + ", version=" +
version + ", type=" + type + ", classifier="
+ classifier + "]";
}
}
+
+ public static class FeatureScanner extends AbstractScanner {
+
+ private final Map<String, Feature> features;
+
+ private final Map<ArtifactId, Feature> included = new
LinkedHashMap<>();
+
+ public FeatureScanner(final Map<String, Feature> features) {
+ this.features = features;
+ }
+
+
+ @Override
+ public void scan() {
+ setupDefaultFilters();
+ setupMatchPatterns();
+
+ for ( Map.Entry<String, Feature> entry : features.entrySet() ) {
+ final String name = entry.getKey();
+ final String[] tokenizedName = tokenizePathToString( name,
File.separator );
+ if ( isIncluded( name, tokenizedName ) ) {
+ if ( !isExcluded( name, tokenizedName ) ) {
+ included.put( entry.getValue().getId(),
entry.getValue() );
+ }
+ }
+ }
+ }
+
+ static String[] tokenizePathToString( String path, String separator )
+ {
+ List<String> ret = new ArrayList<>();
+ StringTokenizer st = new StringTokenizer( path, separator );
+ while ( st.hasMoreTokens() )
+ {
+ ret.add( st.nextToken() );
+ }
+ return ret.toArray( new String[ret.size()] );
+ }
+
+ public Map<ArtifactId, Feature> getIncluded() {
+ return this.included;
+ }
+
+ @Override
+ public String[] getIncludedFiles() {
+ return null;
+ }
+
+ @Override
+ public String[] getIncludedDirectories() {
+ return null;
+ }
+
+ @Override
+ public File getBasedir() {
+ return null;
+ }
+ }
}
diff --git
a/src/test/java/org/apache/sling/feature/maven/mojos/AggregateFeaturesTest.java
b/src/test/java/org/apache/sling/feature/maven/mojos/AggregateFeaturesTest.java
index 8dbdece..e91c89d 100644
---
a/src/test/java/org/apache/sling/feature/maven/mojos/AggregateFeaturesTest.java
+++
b/src/test/java/org/apache/sling/feature/maven/mojos/AggregateFeaturesTest.java
@@ -17,6 +17,7 @@
package org.apache.sling.feature.maven.mojos;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@@ -52,6 +53,7 @@ import org.apache.sling.feature.io.json.FeatureJSONReader;
import org.apache.sling.feature.maven.mojos.AggregateFeatures.FeatureConfig;
import org.junit.After;
import org.junit.Before;
+import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -81,23 +83,24 @@ public class AggregateFeaturesTest {
pluginCallbacks.put(plugin, artifactId);
}
- //@Test
+ @Test
public void testFeatureConfig() {
FeatureConfig fc = new FeatureConfig();
assertEquals(0, fc.includes.size());
assertEquals(0, fc.excludes.size());
- assertNull(fc.location);
assertNull(fc.groupId);
assertNull(fc.artifactId);
assertNull(fc.version);
assertNull(fc.type);
assertNull(fc.classifier);
- fc.setLocation("loc1");
fc.setIncludes("i1");
fc.setIncludes("i2");
fc.setExcludes("e1");
+
+ assertTrue(fc.isDirectory());
+ assertFalse(fc.isArtifact());
fc.setGroupId("gid1");
fc.setArtifactId("aid1");
fc.setVersion("1.2.3");
@@ -107,12 +110,20 @@ public class AggregateFeaturesTest {
assertEquals(Arrays.asList("i1", "i2"), fc.includes);
assertEquals(Collections.singletonList("e1"), fc.excludes);
- assertEquals("loc1", fc.location);
assertEquals("gid1", fc.groupId);
assertEquals("aid1", fc.artifactId);
assertEquals("1.2.3", fc.version);
assertEquals("slingfeature", fc.type);
assertEquals("clf1", fc.classifier);
+
+ assertFalse(fc.isDirectory());
+ assertFalse(fc.isArtifact());
+
+ fc.includes.clear();
+ fc.excludes.clear();
+
+ assertFalse(fc.isDirectory());
+ assertTrue(fc.isArtifact());
}
//@Test
@@ -121,7 +132,6 @@ public class AggregateFeaturesTest {
getClass().getResource("/aggregate-features/dir2").getFile());
FeatureConfig fc = new FeatureConfig();
- fc.setLocation(featuresDir.getAbsolutePath());
Build mockBuild = Mockito.mock(Build.class);
Mockito.when(mockBuild.getDirectory()).thenReturn(tempDir.toString());
@@ -133,7 +143,7 @@ public class AggregateFeaturesTest {
Mockito.when(mockProj.getVersion()).thenReturn("1.2.3-SNAPSHOT");
AggregateFeatures af = new AggregateFeatures();
- af.classifier = "aggregated";
+ af.aggregateClassifier = "aggregated";
af.aggregates = Collections.singletonList(fc);
af.project = mockProj;
@@ -182,7 +192,6 @@ public class AggregateFeaturesTest {
getClass().getResource("/aggregate-features/dir").getFile());
FeatureConfig fc = new FeatureConfig();
- fc.setLocation(featuresDir.getAbsolutePath());
fc.setIncludes("*.json");
fc.setIncludes("*.foobar");
fc.setIncludes("test_z.feature");
@@ -199,7 +208,7 @@ public class AggregateFeaturesTest {
Mockito.when(mockProj.getVersion()).thenReturn("1.2.3-SNAPSHOT");
AggregateFeatures af = new AggregateFeatures();
- af.classifier = "aggregated";
+ af.aggregateClassifier = "aggregated";
af.aggregates = Collections.singletonList(fc);
af.project = mockProj;
@@ -247,7 +256,6 @@ public class AggregateFeaturesTest {
getClass().getResource("/aggregate-features/dir").getFile());
FeatureConfig fc = new FeatureConfig();
- fc.setLocation(featuresDir.getAbsolutePath());
fc.setIncludes("doesnotexist.json");
Build mockBuild = Mockito.mock(Build.class);
@@ -260,7 +268,7 @@ public class AggregateFeaturesTest {
Mockito.when(mockProj.getVersion()).thenReturn("1.2.3-SNAPSHOT");
AggregateFeatures af = new AggregateFeatures();
- af.classifier = "aggregated";
+ af.aggregateClassifier = "aggregated";
af.aggregates = Collections.singletonList(fc);
af.project = mockProj;
@@ -278,7 +286,6 @@ public class AggregateFeaturesTest {
getClass().getResource("/aggregate-features/dir").getFile());
FeatureConfig fc = new FeatureConfig();
- fc.setLocation(featuresDir.getAbsolutePath());
fc.setExcludes("doesnotexist.json");
Build mockBuild = Mockito.mock(Build.class);
@@ -291,7 +298,7 @@ public class AggregateFeaturesTest {
Mockito.when(mockProj.getVersion()).thenReturn("1.2.3-SNAPSHOT");
AggregateFeatures af = new AggregateFeatures();
- af.classifier = "aggregated";
+ af.aggregateClassifier = "aggregated";
af.aggregates = Collections.singletonList(fc);
af.project = mockProj;
@@ -309,18 +316,15 @@ public class AggregateFeaturesTest {
getClass().getResource("/aggregate-features/dir4").getFile());
FeatureConfig fc1 = new FeatureConfig();
- fc1.setLocation(featuresDir.getAbsolutePath());
fc1.setIncludes("test_x.json");
FeatureConfig fc2 = new FeatureConfig();
- fc2.setLocation(featuresDir.getAbsolutePath());
fc2.setIncludes("test_u.json");
fc2.setIncludes("test_y.json");
fc2.setIncludes("test_v.json");
fc2.setIncludes("test_z.json");
FeatureConfig fc3 = new FeatureConfig();
- fc3.setLocation(featuresDir.getAbsolutePath());
fc3.setIncludes("test_t.json");
Build mockBuild = Mockito.mock(Build.class);
@@ -333,7 +337,7 @@ public class AggregateFeaturesTest {
Mockito.when(mockProj.getVersion()).thenReturn("999");
AggregateFeatures af = new AggregateFeatures();
- af.classifier = "agg";
+ af.aggregateClassifier = "agg";
af.aggregates = Arrays.asList(fc1, fc2, fc3);
af.project = mockProj;
@@ -390,7 +394,7 @@ public class AggregateFeaturesTest {
Mockito.when(mockProj.getVersion()).thenReturn("42");
AggregateFeatures af = new AggregateFeatures();
- af.classifier = "mynewfeature";
+ af.aggregateClassifier = "mynewfeature";
af.aggregates = Collections.singletonList(fc);
af.repoSystem = mockRepo;
af.localRepository = Mockito.mock(ArtifactRepository.class);
@@ -450,7 +454,6 @@ public class AggregateFeaturesTest {
getClass().getResource("/aggregate-features/dir3").getFile());
FeatureConfig fc = new FeatureConfig();
- fc.setLocation(featuresDir.getAbsolutePath());
Build mockBuild = Mockito.mock(Build.class);
Mockito.when(mockBuild.getDirectory()).thenReturn(tempDir.toString());
@@ -462,7 +465,7 @@ public class AggregateFeaturesTest {
Mockito.when(mockProj.getVersion()).thenReturn("1.2.3-SNAPSHOT");
AggregateFeatures af = new AggregateFeatures();
- af.classifier = "aggregated";
+ af.aggregateClassifier = "aggregated";
af.aggregates = Collections.singletonList(fc);
af.project = mockProj;