Add build abstraction

This commit adds a build abstraction with a base model that concrete
build systems can reuse.

See gh-814

Co-authored-by: Stephane Nicoll <snicoll@pivotal.io>
This commit is contained in:
Andy Wilkinson 2019-02-07 15:17:18 +01:00 committed by Stephane Nicoll
parent b5ba883b02
commit 1dbed8cdf8
24 changed files with 1728 additions and 0 deletions

View File

@ -0,0 +1,64 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import io.spring.initializr.generator.version.VersionReference;
/**
* A Bill of Materials (BOM) definition to be declared in a project's build configuration.
*
* @author Stephane Nicoll
*/
public class BillOfMaterials {
private final String groupId;
private final String artifactId;
private final VersionReference version;
private final int order;
public BillOfMaterials(String groupId, String artifactId, VersionReference version) {
this(groupId, artifactId, version, Integer.MAX_VALUE);
}
public BillOfMaterials(String groupId, String artifactId, VersionReference version,
int order) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.order = order;
}
public String getGroupId() {
return this.groupId;
}
public String getArtifactId() {
return this.artifactId;
}
public VersionReference getVersion() {
return this.version;
}
public int getOrder() {
return this.order;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.util.LinkedHashMap;
import java.util.function.Function;
import io.spring.initializr.generator.version.VersionReference;
/**
* A {@link BuildItemContainer} implementation for boms.
*
* @author Stephane Nicoll
*/
public class BomContainer extends BuildItemContainer<String, BillOfMaterials> {
BomContainer(Function<String, BillOfMaterials> itemResolver) {
super(new LinkedHashMap<>(), itemResolver);
}
/**
* Register a {@link BillOfMaterials} with the specified {@code id} and a
* {@link Integer#MAX_VALUE} order.
* @param id the id of the bom
* @param groupId the groupId
* @param artifactId the artifactId
* @param version the {@link VersionReference}
*/
public void add(String id, String groupId, String artifactId,
VersionReference version) {
add(id, new BillOfMaterials(groupId, artifactId, version));
}
/**
* Register a {@link BillOfMaterials} with the specified {@code id} and a custom
* order.
* @param id the id of the bom
* @param groupId the groupId
* @param artifactId the artifactId
* @param version the {@link VersionReference}
* @param order the order of the bom
*/
public void add(String id, String groupId, String artifactId,
VersionReference version, int order) {
add(id, new BillOfMaterials(groupId, artifactId, version, order));
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import io.spring.initializr.generator.version.VersionProperty;
/**
* Build configuration for a project.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
*/
public abstract class Build {
private String group;
private String artifact;
private String version = "0.0.1-SNAPSHOT";
private final Map<VersionProperty, String> versionProperties = new TreeMap<>();
private final DependencyContainer dependencies;
private final BomContainer boms;
private final MavenRepositoryContainer repositories;
private final MavenRepositoryContainer pluginRepositories;
protected Build(BuildItemResolver buildItemResolver) {
BuildItemResolver resolver = determineBuildItemResolver(buildItemResolver);
this.dependencies = new DependencyContainer(resolver::resolveDependency);
this.boms = new BomContainer(resolver::resolveBom);
this.repositories = new MavenRepositoryContainer(resolver::resolveRepository);
this.pluginRepositories = new MavenRepositoryContainer(
resolver::resolveRepository);
}
private static BuildItemResolver determineBuildItemResolver(
BuildItemResolver buildItemResolver) {
if (buildItemResolver != null) {
return buildItemResolver;
}
return new SimpleBuildItemResolver((id) -> null, (id) -> null,
(id) -> id.equals("maven-central") ? MavenRepository.MAVEN_CENTRAL
: null);
}
/**
* Return the identifier of the group for the project.
* @return the groupId
*/
public String getGroup() {
return this.group;
}
public void setGroup(String group) {
this.group = group;
}
/**
* Return the identifier of the project.
* @return the artifactId
*/
public String getArtifact() {
return this.artifact;
}
public void setArtifact(String artifact) {
this.artifact = artifact;
}
public String getVersion() {
return this.version;
}
public void setVersion(String version) {
this.version = version;
}
public void addVersionProperty(VersionProperty versionProperty, String version) {
this.versionProperties.put(versionProperty, version);
}
public void addExternalVersionProperty(String propertyName, String version) {
addVersionProperty(VersionProperty.of(propertyName, false), version);
}
public void addInternalVersionProperty(String propertyName, String version) {
addVersionProperty(VersionProperty.of(propertyName, true), version);
}
public Map<VersionProperty, String> getVersionProperties() {
return Collections.unmodifiableMap(this.versionProperties);
}
public DependencyContainer dependencies() {
return this.dependencies;
}
public BomContainer boms() {
return this.boms;
}
public MavenRepositoryContainer repositories() {
return this.repositories;
}
public MavenRepositoryContainer pluginRepositories() {
return this.pluginRepositories;
}
}

View File

@ -0,0 +1,114 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* A container for items.
*
* @param <I> the type of the identifier
* @param <V> the type of the item
* @author Stephane Nicoll
*/
public class BuildItemContainer<I, V> {
private final Map<I, V> items;
private final Function<I, V> itemResolver;
protected BuildItemContainer(Map<I, V> items, Function<I, V> itemResolver) {
this.items = items;
this.itemResolver = itemResolver;
}
/**
* Specify if this container is empty.
* @return {@code true} if no item is registered
*/
public boolean isEmpty() {
return this.items.isEmpty();
}
/**
* Specify if this container has an item with the specified id.
* @param id the id of an item
* @return {@code true} if an item with the specified {@code id} is registered
*/
public boolean has(I id) {
return this.items.containsKey(id);
}
/**
* Return a {@link Stream} of registered identifiers.
* @return a stream of ids
*/
public Stream<I> ids() {
return this.items.keySet().stream();
}
/**
* Return a {@link Stream} of registered items.
* @return a stream of items
*/
public Stream<V> items() {
return this.items.values().stream();
}
/**
* Return the item with the specified {@code id} or {@code null} if no such item
* exists.
* @param id the id of an item
* @return the item or {@code null}
*/
public V get(I id) {
return this.items.get(id);
}
/**
* Lookup the item with the specified {@code id} and register it to this container.
* @param id the id of an item
*/
public void add(I id) {
V item = this.itemResolver.apply(id);
if (item == null) {
throw new IllegalArgumentException("No such value with id '" + id + "'");
}
add(id, item);
}
/**
* Register the specified {@code item} with the specified {@code id}.
* @param id the id of the item
* @param item the item to register
*/
public void add(I id, V item) {
this.items.put(id, item);
}
/**
* Remove the item with the specified {@code id}.
* @param id the id of the item to remove
* @return {@code true} if such an item was registered, {@code false} otherwise
*/
public boolean remove(I id) {
return this.items.remove(id) != null;
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
/**
* Resolve an item of the {@link Build} against an identifer.
*
* @author Stephane Nicoll
*/
public interface BuildItemResolver {
/**
* Resolve the {@link Dependency} with the specified {@code id}.
* @param id the id of the dependency
* @return the matching {@link Dependency} or {@code null} if none is found
*/
Dependency resolveDependency(String id);
/**
* Resolve the {@link BillOfMaterials} with the specified {@code id}.
* @param id the id of the bom
* @return the matching {@link BillOfMaterials} or {@code null} if none is found
*/
BillOfMaterials resolveBom(String id);
/**
* Resolve the {@link MavenRepository repository} with the specified {@code id}.
* @param id the id of the bom
* @return the matching {@link MavenRepository} or {@code null} if none is found
*/
MavenRepository resolveRepository(String id);
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.nio.file.Path;
import java.util.Objects;
import io.spring.initializr.generator.language.Language;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* A build system that can be used by a generated project.
*
* @author Andy Wilkinson
*/
public interface BuildSystem {
/**
* The id of the build system.
* @return the id
*/
String id();
default Path getMainDirectory(Path projectRoot, Language language) {
return projectRoot.resolve("src/main/" + language.id());
}
default Path getTestDirectory(Path projectRoot, Language language) {
return projectRoot.resolve("src/test/" + language.id());
}
static BuildSystem forId(String id) {
return SpringFactoriesLoader
.loadFactories(BuildSystemFactory.class,
BuildSystem.class.getClassLoader())
.stream().map((factory) -> factory.createBuildSystem(id))
.filter(Objects::nonNull).findFirst()
.orElseThrow(() -> new IllegalStateException(
"Unrecognized build system id '" + id + "'"));
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
/**
* A factory for creating a {@link BuildSystem}.
*
* @author Andy Wilkinson
*/
public interface BuildSystemFactory {
/**
* Creates and returns a {@link BuildSystem} for the given id. If the factory does not
* recognise the given {@code id}, {@code null} should be returned.
* @param id the id of the build system
* @return the build system or {@code null}
*/
BuildSystem createBuildSystem(String id);
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import io.spring.initializr.generator.version.VersionReference;
/**
* A dependency to be declared in a project's build configuration.
*
* @author Andy Wilkinson
*/
public class Dependency {
private final String groupId;
private final String artifactId;
private final VersionReference version;
private final DependencyScope scope;
private final String type;
public Dependency(String groupId, String artifactId) {
this(groupId, artifactId, DependencyScope.COMPILE);
}
public Dependency(String groupId, String artifactId, DependencyScope scope) {
this(groupId, artifactId, null, scope);
}
public Dependency(String groupId, String artifactId, VersionReference version,
DependencyScope scope) {
this(groupId, artifactId, version, scope, null);
}
public Dependency(String groupId, String artifactId, VersionReference version,
DependencyScope scope, String type) {
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.scope = scope;
this.type = type;
}
/**
* The group ID of the dependency.
* @return the group ID
*/
public String getGroupId() {
return this.groupId;
}
/**
* The artifact ID of the dependency.
* @return the artifact ID
*/
public String getArtifactId() {
return this.artifactId;
}
/**
* The {@link VersionReference} of the dependency. May be {@code null} for a
* dependency whose version is expected to be provided by dependency management.
* @return the version reference or {@code null}
*/
public VersionReference getVersion() {
return this.version;
}
/**
* The {@link DependencyScope scope} of the dependency.
* @return the scope
*/
public DependencyScope getScope() {
return this.scope;
}
/**
* The type of the dependency. Can be {@code null} to indicate that the default type
* should be used (i.e. {@code jar}).
* @return the type or {@code null}
*/
public String getType() {
return this.type;
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.util.Comparator;
/**
* A {@link Comparator} that orders {@link Dependency dependencies} in a suitable form for
* being referenced in the build.
*
* @author Stephane Nicoll
*/
public class DependencyComparator implements Comparator<Dependency> {
/**
* A default stateless instance.
*/
public static final DependencyComparator INSTANCE = new DependencyComparator();
@Override
public int compare(Dependency o1, Dependency o2) {
if (isSpringBootDependency(o1) && isSpringBootDependency(o2)) {
return o1.getArtifactId().compareTo(o2.getArtifactId());
}
if (isSpringBootDependency(o1)) {
return -1;
}
if (isSpringBootDependency(o2)) {
return 1;
}
int group = o1.getGroupId().compareTo(o2.getGroupId());
if (group != 0) {
return group;
}
return o1.getArtifactId().compareTo(o2.getArtifactId());
}
private boolean isSpringBootDependency(Dependency dependency) {
return dependency.getGroupId().startsWith("org.springframework.boot");
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.util.LinkedHashMap;
import java.util.function.Function;
import io.spring.initializr.generator.version.VersionReference;
/**
* A {@link BuildItemContainer} implementation for dependencies.
*
* @author Stephane Nicoll
*/
public class DependencyContainer extends BuildItemContainer<String, Dependency> {
DependencyContainer(Function<String, Dependency> itemResolver) {
super(new LinkedHashMap<>(), itemResolver);
}
/**
* Register a {@link Dependency} with the specified {@code id} and a managed version.
* @param id the id of the dependency
* @param groupId the groupId
* @param artifactId the artifactId
* @param scope the {@link DependencyScope}
*/
public void add(String id, String groupId, String artifactId, DependencyScope scope) {
add(id, new Dependency(groupId, artifactId, scope));
}
/**
* Register a {@link Dependency} with the specified {@code id}, version and
* {@link DependencyScope scope}.
* @param id the id of the dependency
* @param groupId the groupId
* @param artifactId the artifactId
* @param version the {@link VersionReference}
* @param scope the {@link DependencyScope}
*/
public void add(String id, String groupId, String artifactId,
VersionReference version, DependencyScope scope) {
add(id, new Dependency(groupId, artifactId, version, scope));
}
/**
* Register a {@link Dependency} with the specified {@code id}, version,
* {@link DependencyScope scope} and type.
* @param id the id of the dependency
* @param groupId the groupId
* @param artifactId the artifactId
* @param version the {@link VersionReference}
* @param scope the {@link DependencyScope}
* @param type the artifact type
*/
public void add(String id, String groupId, String artifactId,
VersionReference version, DependencyScope scope, String type) {
add(id, new Dependency(groupId, artifactId, version, scope, type));
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
/**
* The scopes of dependencies supported by project generation.
*
* @author Andy Wilkinson
*/
public enum DependencyScope {
/**
* A dependency that is used as an annotation processor when compiling a project.
*/
ANNOTATION_PROCESSOR,
/**
* A dependency that is used to compile a project.
*/
COMPILE,
/**
* A dependency that is a compile time only dependency and not used at runtime.
*/
COMPILE_ONLY,
/**
* A dependency this is used to run a project.
*/
RUNTIME,
/**
* A dependency that is provided and is used to run the project.
*/
PROVIDED_RUNTIME,
/**
* A dependency that is used to compile a project's tests.
*/
TEST_COMPILE,
/**
* A dependency this is used to run a project's tests.
*/
TEST_RUNTIME
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
/**
* A Maven repository.
*
* @author Andy Wilkinson
*/
public class MavenRepository {
/**
* Maven Central.
*/
public static final MavenRepository MAVEN_CENTRAL = new MavenRepository(
"maven-central", "Maven Central", "https://repo.maven.apache.org/maven2");
private final String id;
private final String name;
private final String url;
private final boolean snapshotsEnabled;
public MavenRepository(String id, String name, String url) {
this(id, name, url, false);
}
public MavenRepository(String id, String name, String url, boolean snapshotsEnabled) {
this.id = id;
this.name = name;
this.url = url;
this.snapshotsEnabled = snapshotsEnabled;
}
public String getId() {
return this.id;
}
public String getName() {
return this.name;
}
public String getUrl() {
return this.url;
}
public boolean isSnapshotsEnabled() {
return this.snapshotsEnabled;
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.util.LinkedHashMap;
import java.util.function.Function;
/**
* A {@link BuildItemContainer} implementation for repositories.
*
* @author Stephane Nicoll
*/
public class MavenRepositoryContainer
extends BuildItemContainer<String, MavenRepository> {
MavenRepositoryContainer(Function<String, MavenRepository> itemResolver) {
super(new LinkedHashMap<>(), itemResolver);
}
/**
* Register the specified {@link MavenRepository repository}.
* @param repository the repository to register
*/
public void add(MavenRepository repository) {
add(repository.getId(), repository);
}
/**
* Register a {@link MavenRepository repository} with snapshots disabled.
* @param id the id of the repository
* @param name the repository name
* @param url the repository url
*/
public void add(String id, String name, String url) {
add(id, name, url, false);
}
/**
* Register a {@link MavenRepository repository} and specify whether snapshots are
* enabled.
* @param id the id of the repository
* @param name the repository name
* @param url the repository url
* @param snapshotsEnabled whether snapshots are enabled
*/
public void add(String id, String name, String url, boolean snapshotsEnabled) {
add(id, new MavenRepository(id, name, url, snapshotsEnabled));
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.util.function.Function;
/**
* A simple {@link BuildItemResolver} implementation.
*
* @author Stephane Nicoll
*/
public class SimpleBuildItemResolver implements BuildItemResolver {
private final Function<String, Dependency> dependencyResolver;
private final Function<String, BillOfMaterials> bomResolver;
private final Function<String, MavenRepository> repositoryResolver;
public SimpleBuildItemResolver(Function<String, Dependency> dependencyResolver,
Function<String, BillOfMaterials> bomResolver,
Function<String, MavenRepository> repositoryResolver) {
this.dependencyResolver = dependencyResolver;
this.bomResolver = bomResolver;
this.repositoryResolver = repositoryResolver;
}
@Override
public Dependency resolveDependency(String id) {
return this.dependencyResolver.apply(id);
}
@Override
public BillOfMaterials resolveBom(String id) {
return this.bomResolver.apply(id);
}
@Override
public MavenRepository resolveRepository(String id) {
return this.repositoryResolver.apply(id);
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Build systems abstraction.
*/
package io.spring.initializr.generator.buildsystem;

View File

@ -0,0 +1,98 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.version;
import java.util.Objects;
/**
* A version reference to either a property or an actual version.
*
* @author Stephane Nicoll
*/
public final class VersionReference {
private final VersionProperty property;
private final String value;
private VersionReference(VersionProperty property, String value) {
this.property = property;
this.value = value;
}
public static VersionReference ofProperty(VersionProperty property) {
return new VersionReference(property, null);
}
public static VersionReference ofProperty(String internalProperty) {
return ofProperty(VersionProperty.of(internalProperty));
}
public static VersionReference ofValue(String value) {
return new VersionReference(null, value);
}
/**
* Specify if this reference defines a property.
* @return {@code true} if this version is backed by a property
*/
public boolean isProperty() {
return this.property != null;
}
/**
* Return the {@link VersionProperty} or {@code null} if this reference is not a
* property.
* @return the version property or {@code null}
*/
public VersionProperty getProperty() {
return this.property;
}
/**
* Return the version of {@code null} if this reference is backed by a property.
* @return the version or {@code null}
*/
public String getValue() {
return this.value;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
VersionReference that = (VersionReference) o;
return Objects.equals(this.property, that.property)
&& Objects.equals(this.value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(this.property, this.value);
}
@Override
public String toString() {
return (this.property != null) ? "${" + this.property.toStandardFormat() + "}"
: this.value;
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import io.spring.initializr.generator.version.VersionReference;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link BomContainer}.
*
* @author Stephane Nicoll
*/
class BomContainerTests {
@Test
void addBom() {
BomContainer container = createTestContainer();
container.add("root", "org.springframework.boot", "spring-boot-dependencies",
VersionReference.ofProperty("spring-boot.version"));
assertThat(container.ids()).containsOnly("root");
assertThat(container.items()).hasSize(1);
assertThat(container.isEmpty()).isFalse();
assertThat(container.has("root")).isTrue();
BillOfMaterials bom = container.get("root");
assertThat(bom).isNotNull();
assertThat(bom.getGroupId()).isEqualTo("org.springframework.boot");
assertThat(bom.getArtifactId()).isEqualTo("spring-boot-dependencies");
assertThat(bom.getVersion())
.isEqualTo(VersionReference.ofProperty("spring-boot.version"));
assertThat(bom.getOrder()).isEqualTo(Integer.MAX_VALUE);
}
@Test
void addBomWithOrder() {
BomContainer container = createTestContainer();
container.add("custom", "com.example", "acme", VersionReference.ofValue("1.0.0"),
42);
assertThat(container.ids()).containsOnly("custom");
assertThat(container.items()).hasSize(1);
assertThat(container.isEmpty()).isFalse();
assertThat(container.has("custom")).isTrue();
BillOfMaterials bom = container.get("custom");
assertThat(bom).isNotNull();
assertThat(bom.getGroupId()).isEqualTo("com.example");
assertThat(bom.getArtifactId()).isEqualTo("acme");
assertThat(bom.getVersion()).isEqualTo(VersionReference.ofValue("1.0.0"));
assertThat(bom.getOrder()).isEqualTo(42);
}
private BomContainer createTestContainer() {
return new BomContainer((id) -> null);
}
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link BuildItemContainer}.
*
* @author Stephane Nicoll
*/
class BuildItemContainerTests {
@Test
void emptyContainer() {
BuildItemContainer<String, String> container = createTestContainer(
new LinkedHashMap<>());
assertThat(container.isEmpty()).isTrue();
assertThat(container.ids()).isEmpty();
assertThat(container.items()).isEmpty();
assertThat(container.get("any")).isNull();
assertThat(container.has("any")).isFalse();
}
@Test
void addElement() {
BuildItemContainer<String, String> container = createTestContainer(
new LinkedHashMap<>());
container.add("test", "value");
assertThat(container.ids()).containsOnly("test");
assertThat(container.items()).containsOnly("value");
assertThat(container.isEmpty()).isFalse();
assertThat(container.get("test")).isEqualTo("value");
assertThat(container.has("test")).isTrue();
}
@Test
void addElementWithSameIdOverrideItem() {
BuildItemContainer<String, String> container = createTestContainer(
new LinkedHashMap<>());
container.add("test", "value");
container.add("test", "another");
assertThat(container.get("test")).isEqualTo("another");
}
@Test
void addByIdWithResolution() {
BuildItemContainer<String, String> container = createTestContainer(
new LinkedHashMap<>(), (id) -> id.equals("test") ? "value" : null);
container.add("test");
assertThat(container.get("test")).isEqualTo("value");
}
@Test
void addByIdWithNoResolution() {
BuildItemContainer<String, String> container = createTestContainer(
new LinkedHashMap<>(), (id) -> id.equals("test") ? "value" : null);
assertThatIllegalArgumentException().isThrownBy(() -> container.add("unknown"))
.withMessageContaining("unknown");
}
@Test
void removeExistingElement() {
BuildItemContainer<String, String> container = createTestContainer(
new LinkedHashMap<>());
container.add("test", "value");
assertThat(container.remove("test")).isTrue();
assertThat(container.ids()).isEmpty();
assertThat(container.items()).isEmpty();
assertThat(container.isEmpty()).isTrue();
}
@Test
void removeUnknownElement() {
BuildItemContainer<String, String> container = createTestContainer(
new LinkedHashMap<>());
container.add("test", "value");
assertThat(container.remove("unknown")).isFalse();
assertThat(container.ids()).containsOnly("test");
assertThat(container.items()).containsOnly("value");
assertThat(container.isEmpty()).isFalse();
}
private BuildItemContainer<String, String> createTestContainer(
Map<String, String> content) {
return createTestContainer(content, (id) -> null);
}
private BuildItemContainer<String, String> createTestContainer(
Map<String, String> content, Function<String, String> itemResolver) {
return new BuildItemContainer<>(content, itemResolver);
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link BuildSystem}.
*
* @author Stephane Nicoll
*/
class BuildSystemTests {
@Test
void unknownBuildSystem() {
assertThatIllegalStateException().isThrownBy(() -> BuildSystem.forId("unknown"))
.withMessageContaining("Unrecognized build system id 'unknown'");
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link Build}.
*
* @author Stephane Nicoll
*/
class BuildTests {
@Test
void buildWithDefaultBuildItemResolver() {
TestBuild build = new TestBuild(null);
Assertions.assertThatIllegalArgumentException()
.isThrownBy(() -> build.dependencies().add("test"))
.withMessageContaining("No such value with id 'test'");
Assertions.assertThatIllegalArgumentException()
.isThrownBy(() -> build.boms().add("another"))
.withMessageContaining("No such value with id 'another'");
Assertions.assertThatIllegalArgumentException()
.isThrownBy(() -> build.repositories().add("repo"))
.withMessageContaining("No such value with id 'repo'");
}
@Test
void buildWithCustomBuildItemResolverResolveDependency() {
BuildItemResolver resolver = mock(BuildItemResolver.class);
Dependency dependency = mock(Dependency.class);
given(resolver.resolveDependency("test")).willReturn(dependency);
TestBuild build = new TestBuild(resolver);
assertThat(build.dependencies().ids()).hasSize(0);
build.dependencies().add("test");
assertThat(build.dependencies().items()).containsExactly(dependency);
}
@Test
void buildWithCustomBuildItemResolverResolveBom() {
BuildItemResolver resolver = mock(BuildItemResolver.class);
BillOfMaterials bom = mock(BillOfMaterials.class);
given(resolver.resolveBom("another")).willReturn(bom);
TestBuild build = new TestBuild(resolver);
assertThat(build.boms().ids()).hasSize(0);
build.boms().add("another");
assertThat(build.boms().items()).containsExactly(bom);
}
@Test
void buildWithCustomBuildItemResolverResolveRepository() {
BuildItemResolver resolver = mock(BuildItemResolver.class);
MavenRepository repository = mock(MavenRepository.class);
given(resolver.resolveRepository("repo")).willReturn(repository);
TestBuild build = new TestBuild(resolver);
assertThat(build.repositories().ids()).hasSize(0);
build.repositories().add("repo");
assertThat(build.repositories().items()).containsExactly(repository);
}
private static class TestBuild extends Build {
TestBuild(BuildItemResolver buildItemResolver) {
super(buildItemResolver);
}
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DependencyComparator}.
*
* @author Stephane Nicoll
*/
class DependencyComparatorTests {
private static final Dependency SPRING_BOOT_A = new Dependency(
"org.springframework.boot", "spring-boot-a");
private static final Dependency SPRING_BOOT_B = new Dependency(
"org.springframework.boot", "spring-boot-b");
private static final Dependency LIB_ALPHA = new Dependency("com.example.alpha",
"test");
private static final Dependency LIB_BETA = new Dependency("com.example.beta", "test");
private final DependencyComparator comparator = new DependencyComparator();
@Test
void compareWithStarters() {
assertThat(this.comparator.compare(SPRING_BOOT_A, SPRING_BOOT_B)).isNegative();
}
@Test
void compareStarterToLib() {
assertThat(this.comparator.compare(SPRING_BOOT_A, LIB_BETA)).isNegative();
}
@Test
void compareLibToStarter() {
assertThat(this.comparator.compare(LIB_ALPHA, SPRING_BOOT_A)).isPositive();
}
@Test
void compareLibDifferentGroupId() {
assertThat(this.comparator.compare(LIB_ALPHA, LIB_BETA)).isNegative();
}
@Test
void compareLibSameGroupId() {
assertThat(this.comparator.compare(LIB_BETA,
new Dependency("com.example.beta", "a"))).isPositive();
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import io.spring.initializr.generator.version.VersionReference;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DependencyContainer}.
*
* @author Stephane Nicoll
*/
class DependencyContainerTests {
@Test
void addDependency() {
DependencyContainer container = createTestContainer();
container.add("web", "org.springframework.boot", "spring-boot-starter-web",
DependencyScope.COMPILE);
assertThat(container.ids()).containsOnly("web");
assertThat(container.items()).hasSize(1);
assertThat(container.isEmpty()).isFalse();
assertThat(container.has("web")).isTrue();
Dependency web = container.get("web");
assertThat(web).isNotNull();
assertThat(web.getGroupId()).isEqualTo("org.springframework.boot");
assertThat(web.getArtifactId()).isEqualTo("spring-boot-starter-web");
assertThat(web.getVersion()).isNull();
assertThat(web.getScope()).isEqualTo(DependencyScope.COMPILE);
}
@Test
void addDependencyWithVersion() {
DependencyContainer container = createTestContainer();
container.add("custom", "com.example", "acme", VersionReference.ofValue("1.0.0"),
DependencyScope.COMPILE);
assertThat(container.ids()).containsOnly("custom");
assertThat(container.items()).hasSize(1);
assertThat(container.isEmpty()).isFalse();
assertThat(container.has("custom")).isTrue();
Dependency custom = container.get("custom");
assertThat(custom).isNotNull();
assertThat(custom.getGroupId()).isEqualTo("com.example");
assertThat(custom.getArtifactId()).isEqualTo("acme");
assertThat(custom.getVersion()).isEqualTo(VersionReference.ofValue("1.0.0"));
assertThat(custom.getScope()).isEqualTo(DependencyScope.COMPILE);
}
private DependencyContainer createTestContainer() {
return new DependencyContainer((id) -> null);
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.buildsystem;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MavenRepositoryContainer}.
*
* @author Stephane Nicoll
*/
class MavenRepositoryContainerTests {
@Test
void addMavenRepository() {
MavenRepositoryContainer container = createTestContainer();
container.add("test", "my repo", "https://example.com/releases");
assertThat(container.ids()).containsOnly("test");
assertThat(container.items()).hasSize(1);
assertThat(container.isEmpty()).isFalse();
assertThat(container.has("test")).isTrue();
MavenRepository repository = container.get("test");
assertThat(repository).isNotNull();
assertThat(repository.getName()).isEqualTo("my repo");
assertThat(repository.getUrl()).isEqualTo("https://example.com/releases");
assertThat(repository.isSnapshotsEnabled()).isFalse();
}
@Test
void addMavenRepositoryInstance() {
MavenRepositoryContainer container = createTestContainer();
MavenRepository instance = new MavenRepository("test", "my repo",
"https://example.com/releases");
container.add(instance);
assertThat(container.ids()).containsOnly("test");
assertThat(container.items()).hasSize(1);
assertThat(container.isEmpty()).isFalse();
assertThat(container.has("test")).isTrue();
MavenRepository repository = container.get("test");
assertThat(repository).isNotNull();
assertThat(repository.getName()).isEqualTo("my repo");
assertThat(repository.getUrl()).isEqualTo("https://example.com/releases");
assertThat(repository.isSnapshotsEnabled()).isFalse();
}
@Test
void addMavenRepositoryWithSnapshotsEnabled() {
MavenRepositoryContainer container = createTestContainer();
container.add("custom", "custom-snapshots", "https://example.com/snapshots",
true);
assertThat(container.ids()).containsOnly("custom");
assertThat(container.items()).hasSize(1);
assertThat(container.isEmpty()).isFalse();
assertThat(container.has("custom")).isTrue();
MavenRepository repository = container.get("custom");
assertThat(repository).isNotNull();
assertThat(repository.getName()).isEqualTo("custom-snapshots");
assertThat(repository.getUrl()).isEqualTo("https://example.com/snapshots");
assertThat(repository.isSnapshotsEnabled()).isTrue();
}
private MavenRepositoryContainer createTestContainer() {
return new MavenRepositoryContainer((id) -> null);
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2012-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.generator.version;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link VersionReference}.
*
* @author Stephane Nicoll
*/
class VersionReferenceTests {
@Test
void ofPropertyWithVersionProperty() {
VersionProperty property = VersionProperty.of("test.version", true);
VersionReference reference = VersionReference.ofProperty(property);
assertThat(reference.isProperty()).isTrue();
assertThat(reference.getProperty()).isEqualTo(property);
assertThat(reference.getValue()).isNull();
assertThat(reference).hasToString("${test.version}");
}
@Test
void ofPropertyWithInternalProperty() {
VersionReference reference = VersionReference.ofProperty("test.version");
assertThat(reference.isProperty()).isTrue();
assertThat(reference.getProperty().toStandardFormat()).isEqualTo("test.version");
assertThat(reference.getValue()).isNull();
assertThat(reference).hasToString("${test.version}");
}
@Test
void ofPropertyWithValue() {
VersionReference reference = VersionReference.ofValue("1.2.3.RELEASE");
assertThat(reference.isProperty()).isFalse();
assertThat(reference.getProperty()).isNull();
assertThat(reference.getValue()).isEqualTo("1.2.3.RELEASE");
assertThat(reference).hasToString("1.2.3.RELEASE");
}
@Test
void equalsWithSameValue() {
assertThat(VersionReference.ofValue("1"))
.isEqualTo(VersionReference.ofValue("1"));
}
@Test
void equalsWithDifferentValue() {
assertThat(VersionReference.ofValue("1"))
.isNotEqualTo(VersionReference.ofValue("2"));
}
@Test
void equalsWithSameProperty() {
assertThat(VersionReference.ofProperty("test.version"))
.isEqualTo(VersionReference.ofProperty("test.version"));
}
@Test
void equalsWithDifferentProperty() {
assertThat(VersionReference.ofProperty("test.version"))
.isNotEqualTo(VersionReference.ofProperty("another.version"));
}
@Test
void equalsWithDifferentPropertyScope() {
assertThat(VersionReference.ofProperty(VersionProperty.of("test.version", false)))
.isNotEqualTo(VersionReference
.ofProperty(VersionProperty.of("test.version", true)));
}
}