first failed attempt
This commit is contained in:
@@ -1,16 +1,15 @@
|
||||
#
|
||||
# Build
|
||||
#
|
||||
FROM openjdk:8-jdk AS build
|
||||
FROM debian:10 AS build
|
||||
|
||||
RUN apt-get update && apt-get -y install maven
|
||||
|
||||
WORKDIR /opt/shipping
|
||||
|
||||
COPY pom.xml /opt/shipping/
|
||||
RUN mvn install
|
||||
|
||||
COPY src /opt/shipping/src/
|
||||
RUN mvn compile
|
||||
RUN mvn package
|
||||
|
||||
#
|
||||
@@ -25,7 +24,7 @@ WORKDIR /opt/shipping
|
||||
ENV CART_ENDPOINT=cart:8080
|
||||
ENV DB_HOST=mysql
|
||||
|
||||
COPY --from=build /opt/shipping/target/shipping-1.0-jar-with-dependencies.jar shipping.jar
|
||||
COPY --from=build /opt/shipping/target/shipping-1.0.jar shipping.jar
|
||||
|
||||
CMD [ "java", "-Xmn256m", "-Xmx768m", "-jar", "shipping.jar" ]
|
||||
|
||||
|
23
shipping/docker-compose.yaml
Normal file
23
shipping/docker-compose.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
version: '3'
|
||||
services:
|
||||
shipping:
|
||||
build:
|
||||
context: .
|
||||
image: foo
|
||||
ports:
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
- mysql
|
||||
networks:
|
||||
- test
|
||||
|
||||
mysql:
|
||||
image: robotshop/rs-mysql-db
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
networks:
|
||||
- test
|
||||
|
||||
|
||||
networks:
|
||||
test:
|
112
shipping/pom.xml
112
shipping/pom.xml
@@ -1,78 +1,60 @@
|
||||
<project>
|
||||
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.2.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>steveww</groupId>
|
||||
<groupId>robotshop</groupId>
|
||||
<artifactId>shipping</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spark Java Sample</name>
|
||||
<name>Shipping Service</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<java.version>1.8</java.version>
|
||||
<start-class>com.instana.robotshop.ShippingApplication</start-class>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.7.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.25</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>c3p0</groupId>
|
||||
<artifactId>c3p0</artifactId>
|
||||
<version>0.9.1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.19</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-dbutils</groupId>
|
||||
<artifactId>commons-dbutils</artifactId>
|
||||
<version>1.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.5</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>org.steveww.spark.Main</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
72
shipping/src/main/java/com/instana/robotshop/City.java
Normal file
72
shipping/src/main/java/com/instana/robotshop/City.java
Normal file
@@ -0,0 +1,72 @@
|
||||
package com.instana.robotshop;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/*
|
||||
* Bean for City
|
||||
*/
|
||||
@Entity
|
||||
public class City {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
|
||||
private String code;
|
||||
private String city;
|
||||
private String name;
|
||||
private String region;
|
||||
private double latitude;
|
||||
private double longitude;
|
||||
|
||||
public String getCode() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return this.city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return this.region;
|
||||
}
|
||||
|
||||
public void setRegion(String code) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return this.latitude;
|
||||
}
|
||||
|
||||
public void setLatitude(double latitude) {
|
||||
this.latitude = latitude;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return this.longitude;
|
||||
}
|
||||
|
||||
public void setLongitude(double longitude) {
|
||||
this.longitude = longitude;
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.instana.robotshop;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
|
||||
|
||||
@RepositoryRestResource(collectionResourceRel = "cities", path = "city")
|
||||
public interface CityRepository extends PagingAndSortingRepository<City, Long> {
|
||||
List<City> findByCode(@Param("code") String code);
|
||||
}
|
29
shipping/src/main/java/com/instana/robotshop/JpaConfig.java
Normal file
29
shipping/src/main/java/com/instana/robotshop/JpaConfig.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.instana.robotshop;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import javax.sql.DataSource;
|
||||
import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@Configuration
|
||||
public class JpaConfig {
|
||||
private static final Logger logger = LoggerFactory.getLogger(JpaConfig.class);
|
||||
|
||||
@Bean
|
||||
public DataSource getDataSource() {
|
||||
String JDBC_URL = String.format("jdbc:mysql://%s/cities?useSSL=false&autoReconnect=true", System.getenv("DB_HOST") == null ? "mysql" : System.getenv("DB_HOST"));
|
||||
|
||||
logger.info("jdbc url {}", JDBC_URL);
|
||||
|
||||
DataSourceBuilder bob = DataSourceBuilder.create();
|
||||
|
||||
bob.driverClassName("com.mysql.jdbc.Driver");
|
||||
bob.url(JDBC_URL);
|
||||
bob.username("shipping");
|
||||
bob.password("secret");
|
||||
|
||||
return bob.build();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.instana.robotshop;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class ShippingApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(ShippingApplication.class, args);
|
||||
}
|
||||
}
|
@@ -1,43 +0,0 @@
|
||||
package org.steveww.spark;
|
||||
|
||||
public class Location {
|
||||
private double latitude;
|
||||
private double longitude;
|
||||
|
||||
public Location(double latitude, double longitude) {
|
||||
this.latitude = latitude;
|
||||
this.longitude = longitude;
|
||||
}
|
||||
|
||||
public double getLatitude() {
|
||||
return this.latitude;
|
||||
}
|
||||
|
||||
public double getLongitude() {
|
||||
return this.longitude;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the distance between this location and the target location.
|
||||
* Use decimal lat/long degrees
|
||||
* Formula is Haversine https://www.movable-type.co.uk/scripts/latlong.html
|
||||
**/
|
||||
public long getDistance(double targetLatitude, double targetLongitude) {
|
||||
double distance = 0.0;
|
||||
double earthRadius = 6371e3; // meters
|
||||
|
||||
// convert to radians
|
||||
double latitudeR = Math.toRadians(this.latitude);
|
||||
double targetLatitudeR = Math.toRadians(targetLatitude);
|
||||
// difference in Radians
|
||||
double diffLatR = Math.toRadians(targetLatitude - this.latitude);
|
||||
double diffLongR = Math.toRadians(targetLongitude - this.longitude);
|
||||
|
||||
double a = Math.sin(diffLatR / 2.0) * Math.sin(diffLatR / 2.0) + Math.cos(latitudeR) * Math.cos(targetLatitudeR) * Math.sin(diffLongR / 2.0) * Math.sin(diffLongR);
|
||||
|
||||
double c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a));
|
||||
|
||||
return (long)Math.rint(earthRadius * c / 1000.0);
|
||||
}
|
||||
}
|
@@ -1,252 +0,0 @@
|
||||
package org.steveww.spark;
|
||||
|
||||
import com.mchange.v2.c3p0.ComboPooledDataSource;
|
||||
import spark.Spark;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.BasicHttpParams;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import org.apache.commons.dbutils.QueryRunner;
|
||||
import org.apache.commons.dbutils.handlers.MapListHandler;
|
||||
import org.apache.commons.dbutils.DbUtils;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.Types;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Main {
|
||||
private static String CART_URL = null;
|
||||
private static String JDBC_URL = null;
|
||||
private static Logger logger = LoggerFactory.getLogger(Main.class);
|
||||
private static ComboPooledDataSource cpds = null;
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Get ENV configuration values
|
||||
CART_URL = String.format("http://%s/shipping/", System.getenv("CART_ENDPOINT") != null ? System.getenv("CART_ENDPOINT") : "cart");
|
||||
JDBC_URL = String.format("jdbc:mysql://%s/cities?useSSL=false&autoReconnect=true", System.getenv("DB_HOST") != null ? System.getenv("DB_HOST") : "mysql");
|
||||
|
||||
//
|
||||
// Create database connector
|
||||
// TODO - might need a retry loop here
|
||||
//
|
||||
try {
|
||||
cpds = new ComboPooledDataSource();
|
||||
cpds.setDriverClass( "com.mysql.jdbc.Driver" ); //loads the jdbc driver
|
||||
cpds.setJdbcUrl( JDBC_URL );
|
||||
cpds.setUser("shipping");
|
||||
cpds.setPassword("secret");
|
||||
// some config
|
||||
cpds.setMinPoolSize(5);
|
||||
cpds.setAcquireIncrement(5);
|
||||
cpds.setMaxPoolSize(20);
|
||||
cpds.setMaxStatements(180);
|
||||
}
|
||||
catch(Exception e) {
|
||||
logger.error("Database Exception", e);
|
||||
}
|
||||
|
||||
// Spark
|
||||
Spark.port(8080);
|
||||
|
||||
Spark.get("/health", (req, res) -> "OK");
|
||||
|
||||
Spark.get("/count", (req, res) -> {
|
||||
String data;
|
||||
try {
|
||||
data = queryToJson("select count(*) as count from cities");
|
||||
res.header("Content-Type", "application/json");
|
||||
} catch(Exception e) {
|
||||
logger.error("count", e);
|
||||
res.status(500);
|
||||
data = "ERROR";
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
Spark.get("/codes", (req, res) -> {
|
||||
String data;
|
||||
try {
|
||||
String query = "select code, name from codes order by name asc";
|
||||
data = queryToJson(query);
|
||||
res.header("Content-Type", "application/json");
|
||||
} catch(Exception e) {
|
||||
logger.error("codes", e);
|
||||
res.status(500);
|
||||
data = "ERROR";
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
// needed for load gen script
|
||||
Spark.get("/cities/:code", (req, res) -> {
|
||||
String data;
|
||||
try {
|
||||
String query = "select uuid, name from cities where country_code = ?";
|
||||
logger.info("Query " + query);
|
||||
data = queryToJson(query, req.params(":code"));
|
||||
res.header("Content-Type", "application/json");
|
||||
} catch(Exception e) {
|
||||
logger.error("cities", e);
|
||||
res.status(500);
|
||||
data = "ERROR";
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
Spark.get("/match/:code/:text", (req, res) -> {
|
||||
String data;
|
||||
try {
|
||||
String query = "select uuid, name from cities where country_code = ? and city like ? order by name asc limit 10";
|
||||
logger.info("Query " + query);
|
||||
data = queryToJson(query, req.params(":code"), req.params(":text") + "%");
|
||||
res.header("Content-Type", "application/json");
|
||||
} catch(Exception e) {
|
||||
logger.error("match", e);
|
||||
res.status(500);
|
||||
data = "ERROR";
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
Spark.get("/calc/:uuid", (req, res) -> {
|
||||
double homeLat = 51.164896;
|
||||
double homeLong = 7.068792;
|
||||
String data;
|
||||
|
||||
Location location = getLocation(req.params(":uuid"));
|
||||
Ship ship = new Ship();
|
||||
if(location != null) {
|
||||
long distance = location.getDistance(homeLat, homeLong);
|
||||
// charge 0.05 Euro per km
|
||||
// try to avoid rounding errors
|
||||
double cost = Math.rint(distance * 5) / 100.0;
|
||||
ship.setDistance(distance);
|
||||
ship.setCost(cost);
|
||||
res.header("Content-Type", "application/json");
|
||||
data = new Gson().toJson(ship);
|
||||
} else {
|
||||
data = "no location";
|
||||
logger.warn(data);
|
||||
res.status(400);
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
Spark.post("/confirm/:id", (req, res) -> {
|
||||
logger.info("confirm " + req.params(":id") + " - " + req.body());
|
||||
String cart = addToCart(req.params(":id"), req.body());
|
||||
logger.info("new cart " + cart);
|
||||
|
||||
if(cart.equals("")) {
|
||||
res.status(404);
|
||||
} else {
|
||||
res.header("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
return cart;
|
||||
});
|
||||
|
||||
logger.info("Ready");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Query to Json - QED
|
||||
**/
|
||||
private static String queryToJson(String query, Object ... args) {
|
||||
List<Map<String, Object>> listOfMaps = null;
|
||||
try {
|
||||
QueryRunner queryRunner = new QueryRunner(cpds);
|
||||
listOfMaps = queryRunner.query(query, new MapListHandler(), args);
|
||||
} catch (SQLException se) {
|
||||
throw new RuntimeException("Couldn't query the database.", se);
|
||||
}
|
||||
|
||||
return new Gson().toJson(listOfMaps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case for location, dont want Json
|
||||
**/
|
||||
private static Location getLocation(String uuid) {
|
||||
Location location = null;
|
||||
Connection conn = null;
|
||||
PreparedStatement stmt = null;
|
||||
ResultSet rs = null;
|
||||
String query = "select latitude, longitude from cities where uuid = ?";
|
||||
|
||||
try {
|
||||
conn = cpds.getConnection();
|
||||
stmt = conn.prepareStatement(query);
|
||||
stmt.setInt(1, Integer.parseInt(uuid));
|
||||
rs = stmt.executeQuery();
|
||||
while(rs.next()) {
|
||||
location = new Location(rs.getDouble(1), rs.getDouble(2));
|
||||
break;
|
||||
}
|
||||
} catch(Exception e) {
|
||||
logger.error("Location exception", e);
|
||||
} finally {
|
||||
DbUtils.closeQuietly(conn, stmt, rs);
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
private static String addToCart(String id, String data) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
DefaultHttpClient httpClient = null;
|
||||
try {
|
||||
// set timeout to 5 secs
|
||||
HttpParams httpParams = new BasicHttpParams();
|
||||
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
|
||||
|
||||
httpClient = new DefaultHttpClient(httpParams);
|
||||
HttpPost postRequest = new HttpPost(CART_URL + id);
|
||||
StringEntity payload = new StringEntity(data);
|
||||
payload.setContentType("application/json");
|
||||
postRequest.setEntity(payload);
|
||||
HttpResponse res = httpClient.execute(postRequest);
|
||||
|
||||
if(res.getStatusLine().getStatusCode() == 200) {
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(res.getEntity().getContent()));
|
||||
String line;
|
||||
while((line = in.readLine()) != null) {
|
||||
buffer.append(line);
|
||||
}
|
||||
} else {
|
||||
logger.warn("Failed with code: " + res.getStatusLine().getStatusCode());
|
||||
}
|
||||
} catch(Exception e) {
|
||||
logger.error("http client exception", e);
|
||||
} finally {
|
||||
if(httpClient != null) {
|
||||
httpClient.getConnectionManager().shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
@@ -1,36 +0,0 @@
|
||||
package org.steveww.spark;
|
||||
|
||||
/**
|
||||
* Bean to hold shipping information
|
||||
**/
|
||||
public class Ship {
|
||||
private long distance;
|
||||
private double cost;
|
||||
|
||||
public Ship() {
|
||||
this.distance = 0;
|
||||
this.cost = 0.0;
|
||||
}
|
||||
|
||||
public Ship(long distnace, double cost) {
|
||||
this.distance = distance;
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
public void setDistance(long distance) {
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
public void setCost(double cost) {
|
||||
this.cost = cost;
|
||||
}
|
||||
|
||||
public long getDistance() {
|
||||
return this.distance;
|
||||
}
|
||||
|
||||
public double getCost() {
|
||||
return this.cost;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user