Commit 39d02362 authored by Kohler, Jochen's avatar Kohler, Jochen

Merge branch 'version_8' into 'develop'

Version 8

See merge request !8
parents 1084aceb 6fe46933
......@@ -36,5 +36,9 @@
<module>../version_7.autoconfig-starter</module>
<module>../version_7.client-sales</module>
<module>../version_7.client-service</module>
<module>../version_8.autoconfig-starter</module>
<module>../version_8.common</module>
<module>../version_8.client-sales</module>
<module>../version_8.client-service</module>
</modules>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.exxeta.demo.cars</groupId>
<artifactId>autoconfig-starter</artifactId>
<version>0.8.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>com.exxeta.demo.cars</groupId>
<artifactId>common</artifactId>
<version>0.8.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-spring-service-connector</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-cloudfoundry-connector</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package com.exxeta.demo.cars.common;
import com.exxeta.demo.cars.common.api.CustomerController;
import com.exxeta.demo.cars.common.service.CustomerService;
import com.exxeta.demo.cars.common.service.VehicleService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import static org.springframework.util.StringUtils.isEmpty;
/**
* @author Jan Hauer (EXXETA AG)
*/
@Configuration
@ConditionalOnClass(RestTemplateBuilder.class)
@EnableConfigurationProperties(ServerProperties.class)
public class AutoConfiguration {
@Bean
public ServerConfig serverConfigLocal(ServerProperties properties) {
return new ServerConfig(properties.getUrl(),properties.getUser(),properties.getPassword());
}
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder, ServerConfig serverConfig) {
return builder.basicAuthorization(serverConfig.getUser(), serverConfig.getPassword()) //
.rootUri(serverConfig.getUrl()) //
.build();
}
@Bean
@ConditionalOnMissingBean
public CustomerService customerService(RestTemplate restTemplate) {
return new CustomerService(restTemplate);
}
@Bean
@ConditionalOnMissingBean
public VehicleService vehicleService(RestTemplate restTemplate) {
return new VehicleService(restTemplate);
}
@Bean
@ConditionalOnMissingBean
public CustomerController customerController(CustomerService customerService) {
return new CustomerController(customerService);
}
@Bean
InitializingBean checkIfConfigurationIsComplete(ServerConfig config) {
return () -> {
if (isEmpty(config.getUrl()) || isEmpty(config.getUser()) || isEmpty(config.getPassword())) {
throw new IncompleteConfigurationException(config);
}
};
}
}
\ No newline at end of file
package com.exxeta.demo.cars.common;
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.cloud.config.java.AbstractCloudConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class CloudConfiguration extends AbstractCloudConfig {
@Bean
@Primary
public ServerConfig serverConfigCloud(){
return connectionFactory().service(ServerConfig.class);
}
}
\ No newline at end of file
package com.exxeta.demo.cars.common;
import org.springframework.cloud.cloudfoundry.CloudFoundryServiceInfoCreator;
import org.springframework.cloud.cloudfoundry.Tags;
import java.util.Map;
public class CloudFoundryServerInfoCreator extends CloudFoundryServiceInfoCreator<ServerInfo> {
public CloudFoundryServerInfoCreator() {
super(new Tags(), "cars");
}
@Override
public ServerInfo createServiceInfo(Map<String, Object> serviceData) {
return new ServerInfo(getId(serviceData),getUriFromCredentials(getCredentials(serviceData)));
}
}
package com.exxeta.demo.cars.common;
/**
* @author Jan Hauer (EXXETA AG)
*/
class IncompleteConfigurationException extends RuntimeException {
private static final String TEMPLATE = "Configuration for server connection failed due to missing values." +
"\n\nProvided values:" +
"\n\nurl:<%s>\nuser:<%s>\npassword:<%s>";
IncompleteConfigurationException(ServerConfig config){
super(String.format(TEMPLATE,config.getUrl(),config.getUser(),config.getPassword()));
}
}
package com.exxeta.demo.cars.common;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
/**
* @author Jan Hauer (EXXETA AG)
*/
public class IncompleteConfigurationFailureAnalyzer extends AbstractFailureAnalyzer<IncompleteConfigurationException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, IncompleteConfigurationException cause) {
return new FailureAnalysis(cause.getLocalizedMessage(), "Provide values for url, user and password.", cause);
}
}
package com.exxeta.demo.cars.common;
import lombok.Getter;
import java.util.Objects;
import java.util.Optional;
/**
* @author Jan Hauer (EXXETA AG)
*/
@Getter
public class ServerConfig {
public static final String DEFAULT_URL = "http://localhost:9001";
/**
* Base url for cars server
*/
private final String url;
/**
* user name for cars server
*/
private final String user;
/**
* password for cars server
*/
private final String password;
public ServerConfig(String url, String user, String password) {
this.url = Optional.ofNullable(url).orElse(ServerConfig.DEFAULT_URL);
this.user = Objects.requireNonNull(user);
this.password = Objects.requireNonNull(password);
}
}
package com.exxeta.demo.cars.common;
import org.springframework.cloud.service.AbstractServiceConnectorCreator;
import org.springframework.cloud.service.ServiceConnectorConfig;
public class ServerConnectionCreator extends AbstractServiceConnectorCreator<ServerConfig, ServerInfo> {
@Override
public ServerConfig create(ServerInfo serverInfo, ServiceConnectorConfig serviceConnectorConfig) {
return new ServerConfig(serverInfo.getUrl(),serverInfo.getUserName(),serverInfo.getPassword());
}
}
package com.exxeta.demo.cars.common;
import org.springframework.cloud.service.UriBasedServiceInfo;
import java.util.Optional;
public class ServerInfo extends UriBasedServiceInfo {
public ServerInfo(String id, String uriString) {
super(id, uriString);
}
public String getUrl() {
return getScheme() + "://" + getHost() + Optional.ofNullable(getPath()).map("/"::concat).orElse("");
}
}
package com.exxeta.demo.cars.common;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author Jan Hauer (EXXETA AG)
*/
@Data
@ConfigurationProperties(prefix = "com.exxeta.demo.cars.server")
public class ServerProperties {
/**
* Base url for cars server
*/
private String url;
/**
* user name for cars server
*/
private String user;
/**
* password for cars server
*/
private String password;
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.exxeta.demo.cars.common.CloudConfiguration,\
com.exxeta.demo.cars.common.AutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=com.exxeta.demo.cars.common.IncompleteConfigurationFailureAnalyzer
\ No newline at end of file
package com.exxeta.demo.cars.common
import com.exxeta.demo.cars.common.api.CustomerController
import com.exxeta.demo.cars.common.service.CustomerService
import com.exxeta.demo.cars.common.service.VehicleService
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import spock.lang.Specification
/**
* @author Jan Hauer (EXXETA AG)
*/
@SpringBootTest
class AutoConfigurationSpec extends Specification {
@Autowired(required = false)
private ServerConfig config
@Autowired(required = false)
private VehicleService vehicleService
@Autowired(required = false)
private CustomerService customerService
@Autowired(required = false)
private CustomerController customerController
def "config is provided"() {
expect: "server config is not null"
config
and: "local config is set"
config.user == "user"
config.password == "password"
and: "url has default value"
config.url == ServerConfig.DEFAULT_URL
}
def "vehicle service is provided"() {
expect: "vehicle service is not null"
vehicleService
}
def "customer service is provided"() {
expect: "customer service is not null"
customerService
}
def "customer controller is provided"() {
expect: "customer controller is not null"
customerController
}
}
package com.exxeta.demo.cars.common
import org.springframework.boot.autoconfigure.SpringBootApplication
/**
* @author Jan Hauer (EXXETA AG)
*/
@SpringBootApplication
class TestApplication {
}
com.exxeta.demo.cars.server.user=user
com.exxeta.demo.cars.server.password=password
\ No newline at end of file
# PCF-Sales-Portal Beispiel
Alle Konfiguration sollen in der Umgebung definiert werden.
Daher deployen wir die App zunächst ohne sie zu starten, setzen dann die Umgebungsvariablen und starten sie schließlich.
### Deployen ohne Start:
```bash
cf push manifest.yml --no-start
```
### Setzen der Variablen:
```bash
cf set-env sales-portal SPRING_SECURITY_USER_NAME ((user-name))
cf set-env sales-portal SPRING_SECURITY_USER_PASSWORD ((user-password)
cf set-env sales-portal COM_EXXETA_DEMO_CARS_SERVER_USER ((server-user))
cf set-env sales-portal COM_EXXETA_DEMO_CARS_SERVER_PASSWORD ((server-password)
```
##### Dev:
* user-name: user
* user-password: test
* server-user: sales
* server-password: sales
##### Int
* user-name: user
* user-password: 32gj9j
* server-user: sales
* server-password: g]N>n`${"MFP'-4C
##### Prod
* user-name: user
* user-password: x5&VaCm+7kBxj3
* server-user: u2340
* server-password: r-M3,Wgqj5R#q]eR
### Starten der Anwendung:
```bash
cf start sales-portal
```
\ No newline at end of file
---
applications:
- name: sales-portal
path: target/client-sales-0.8.0-SNAPSHOT.jar
services:
- cars-server
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.exxeta.demo.cars</groupId>
<artifactId>client-sales</artifactId>
<description>Demo client for service</description>
<version>0.8.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.exxeta.demo.cars</groupId>
<artifactId>autoconfig-starter</artifactId>
<version>0.8.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.exxeta.demo.cars.sales;
import com.exxeta.demo.cars.common.ServerConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class SalesApplication {
private final ServerConfig config;
public SalesApplication(ServerConfig config) {
this.config = config;
}
@GetMapping("/config")
public ServerConfig getConfig(){
return config;
}
public static void main(String[] args) {
SpringApplication.run(SalesApplication.class, args);
}
}
package com.exxeta.demo.cars.sales;
import com.exxeta.demo.cars.common.model.Customer;
import com.exxeta.demo.cars.common.model.Vehicle;
import com.exxeta.demo.cars.common.service.CustomerService;
import com.exxeta.demo.cars.common.service.VehicleService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author Jan Hauer (EXXETA AG)
*/
@RestController
@RequestMapping("/vehicles")
public class SalesController {
private final VehicleService vehicleService;
private final CustomerService customerService;
public SalesController(VehicleService vehicleService, CustomerService customerService) {
this.vehicleService = vehicleService;
this.customerService = customerService;
}
@GetMapping("/")
public List<Vehicle> getVehicles() {
return vehicleService.getAll();
}
@GetMapping("/available")
public List<Vehicle> getAvailableVehicles() {
return getVehicles().parallelStream() //
.filter(((Predicate<Vehicle>) Vehicle::isSold).negate()) //
.collect(Collectors.toList());
}