Merge pull request #9937 from ashleyfrieze/BAEL-3998-aws-lambda-hibernate

BAEL-3998-Aws Lambda with Hibernate
This commit is contained in:
Eric Martin 2020-09-10 20:15:16 -05:00 committed by GitHub
commit fb479aca15
22 changed files with 566 additions and 83 deletions

100
aws-lambda/lambda/pom.xml Normal file
View File

@ -0,0 +1,100 @@
<?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>
<artifactId>aws-lambda-examples</artifactId>
<version>0.1.0-SNAPSHOT</version>
<name>aws-lambda-examples</name>
<packaging>jar</packaging>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../</relativePath>
</parent>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>${aws-java-sdk.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
<version>${aws-java-sdk.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>${aws-lambda-java-core.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>${aws-lambda-java-events.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>${json-simple.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<json-simple.version>1.1.1</json-simple.version>
<commons-io.version>2.5</commons-io.version>
<aws-lambda-java-events.version>1.3.0</aws-lambda-java-events.version>
<aws-lambda-java-core.version>1.2.0</aws-lambda-java-core.version>
<gson.version>2.8.2</gson.version>
<aws-java-sdk.version>1.11.241</aws-java-sdk.version>
<maven-shade-plugin.version>3.0.0</maven-shade-plugin.version>
</properties>
</project>

View File

@ -5,95 +5,19 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 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> <modelVersion>4.0.0</modelVersion>
<artifactId>aws-lambda</artifactId> <artifactId>aws-lambda</artifactId>
<version>0.1.0-SNAPSHOT</version>
<name>aws-lambda</name> <name>aws-lambda</name>
<packaging>jar</packaging> <packaging>pom</packaging>
<parent> <parent>
<groupId>com.baeldung</groupId> <groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId> <artifactId>parent-modules</artifactId>
<version>1.0.0-SNAPSHOT</version> <version>1.0.0-SNAPSHOT</version>
<relativePath>../</relativePath>
</parent> </parent>
<dependencies> <modules>
<dependency> <module>lambda</module>
<groupId>com.amazonaws</groupId> <module>shipping-tracker/ShippingFunction</module>
<artifactId>aws-java-sdk-dynamodb</artifactId> </modules>
<version>${aws-java-sdk.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
<version>${aws-java-sdk.version}</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>${aws-lambda-java-core.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>${aws-lambda-java-events.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>${json-simple.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<json-simple.version>1.1.1</json-simple.version>
<commons-io.version>2.5</commons-io.version>
<aws-lambda-java-events.version>1.3.0</aws-lambda-java-events.version>
<aws-lambda-java-core.version>1.2.0</aws-lambda-java-core.version>
<gson.version>2.8.2</gson.version>
<aws-java-sdk.version>1.11.241</aws-java-sdk.version>
<maven-shade-plugin.version>3.0.0</maven-shade-plugin.version>
</properties>
</project> </project>

View File

@ -0,0 +1 @@
.aws-sam/

View File

@ -0,0 +1,73 @@
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung</groupId>
<artifactId>ShippingFunction</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>Shipping Tracker Lambda Function</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<hibernate.version>5.4.21.Final</hibernate.version>
</properties>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-events</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-hikaricp</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.16</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.1</version>
<configuration>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,108 @@
package com.baeldung.lambda.shipping;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import java.util.HashMap;
import java.util.Map;
import static org.hibernate.cfg.AvailableSettings.*;
import static org.hibernate.cfg.AvailableSettings.PASS;
/**
* Handler for requests to Lambda function.
*/
public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
try (SessionFactory sessionFactory = createSessionFactory()) {
ShippingService service = new ShippingService(sessionFactory, new ShippingDao());
return routeRequest(input, service);
}
}
private APIGatewayProxyResponseEvent routeRequest(APIGatewayProxyRequestEvent input, ShippingService service) {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put("X-Custom-Header", "application/json");
Object result = "OK";
switch (input.getResource()) {
case "/consignment":
result = service.createConsignment(
fromJson(input.getBody(), Consignment.class));
break;
case "/consignment/{id}":
result = service.view(input.getPathParameters().get("id"));
break;
case "/consignment/{id}/item":
service.addItem(input.getPathParameters().get("id"),
fromJson(input.getBody(), Item.class));
break;
case "/consignment/{id}/checkin":
service.checkIn(input.getPathParameters().get("id"),
fromJson(input.getBody(), Checkin.class));
break;
}
return new APIGatewayProxyResponseEvent()
.withHeaders(headers)
.withStatusCode(200)
.withBody(toJson(result));
}
private static <T> String toJson(T object) {
try {
return OBJECT_MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
private static <T> T fromJson(String json, Class<T> type) {
try {
return OBJECT_MAPPER.readValue(json, type);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
private static SessionFactory createSessionFactory() {
Map<String, String> settings = new HashMap<>();
settings.put(URL, System.getenv("DB_URL"));
settings.put(DIALECT, "org.hibernate.dialect.PostgreSQLDialect");
settings.put(DEFAULT_SCHEMA, "shipping");
settings.put(DRIVER, "org.postgresql.Driver");
settings.put(USER, System.getenv("DB_USER"));
settings.put(PASS, System.getenv("DB_PASSWORD"));
settings.put("hibernate.hikari.connectionTimeout", "20000");
settings.put("hibernate.hikari.minimumIdle", "1");
settings.put("hibernate.hikari.maximumPoolSize", "2");
settings.put("hibernate.hikari.idleTimeout", "30000");
// commented out as we only need them on first use
// settings.put(HBM2DDL_AUTO, "create-only");
// settings.put(HBM2DDL_DATABASE_ACTION, "create");
StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.applySettings(settings)
.build();
return new MetadataSources(registry)
.addAnnotatedClass(Consignment.class)
.addAnnotatedClass(Item.class)
.addAnnotatedClass(Checkin.class)
.buildMetadata()
.buildSessionFactory();
}
}

View File

@ -0,0 +1,28 @@
package com.baeldung.lambda.shipping;
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable
public class Checkin {
private String timeStamp;
private String location;
@Column(name = "timestamp")
public String getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
}
@Column(name = "location")
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
}

View File

@ -0,0 +1,77 @@
package com.baeldung.lambda.shipping;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
import static javax.persistence.FetchType.EAGER;
@Entity(name = "consignment")
@Table(name = "consignment")
public class Consignment {
private String id;
private String source;
private String destination;
private boolean isDelivered;
private List<Item> items = new ArrayList<>();
private List<Checkin> checkins = new ArrayList<>();
@Id
@Column(name = "consignment_id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Column(name = "source")
public String getSource() {
return source;
}
public void setSource(String source) {
this.source = source;
}
@Column(name = "destination")
public String getDestination() {
return destination;
}
public void setDestination(String destination) {
this.destination = destination;
}
@Column(name = "delivered", columnDefinition = "boolean")
public boolean isDelivered() {
return isDelivered;
}
public void setDelivered(boolean delivered) {
isDelivered = delivered;
}
@ElementCollection(fetch = EAGER)
@CollectionTable(name = "consignment_item", joinColumns = @JoinColumn(name = "consignment_id"))
@OrderColumn(name = "item_index")
public List<Item> getItems() {
return items;
}
public void setItems(List<Item> items) {
this.items = items;
}
@ElementCollection(fetch = EAGER)
@CollectionTable(name = "consignment_checkin", joinColumns = @JoinColumn(name = "consignment_id"))
@OrderColumn(name = "checkin_index")
public List<Checkin> getCheckins() {
return checkins;
}
public void setCheckins(List<Checkin> checkins) {
this.checkins = checkins;
}
}

View File

@ -0,0 +1,38 @@
package com.baeldung.lambda.shipping;
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable
public class Item {
private String location;
private String description;
private String timeStamp;
@Column(name = "location")
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
@Column(name = "description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Column(name = "timestamp")
public String getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.lambda.shipping;
import org.hibernate.Session;
import org.hibernate.Transaction;
import java.util.Optional;
public class ShippingDao {
/**
* Save a consignment to the database
* @param consignment the consignment to save
*/
public void save(Session session, Consignment consignment) {
Transaction transaction = session.beginTransaction();
session.save(consignment);
transaction.commit();
}
/**
* Find a consignment in the database by id
*/
public Optional<Consignment> find(Session session, String id) {
return Optional.ofNullable(session.get(Consignment.class, id));
}
}

View File

@ -0,0 +1,62 @@
package com.baeldung.lambda.shipping;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import java.util.Comparator;
import java.util.UUID;
public class ShippingService {
private SessionFactory sessionFactory;
private ShippingDao shippingDao;
public ShippingService(SessionFactory sessionFactory, ShippingDao shippingDao) {
this.sessionFactory = sessionFactory;
this.shippingDao = shippingDao;
}
public String createConsignment(Consignment consignment) {
try (Session session = sessionFactory.openSession()) {
consignment.setDelivered(false);
consignment.setId(UUID.randomUUID().toString());
shippingDao.save(session, consignment);
return consignment.getId();
}
}
public void addItem(String consignmentId, Item item) {
try (Session session = sessionFactory.openSession()) {
shippingDao.find(session, consignmentId)
.ifPresent(consignment -> addItem(session, consignment, item));
}
}
private void addItem(Session session, Consignment consignment, Item item) {
consignment.getItems()
.add(item);
shippingDao.save(session, consignment);
}
public void checkIn(String consignmentId, Checkin checkin) {
try (Session session = sessionFactory.openSession()) {
shippingDao.find(session, consignmentId)
.ifPresent(consignment -> checkIn(session, consignment, checkin));
}
}
private void checkIn(Session session, Consignment consignment, Checkin checkin) {
consignment.getCheckins().add(checkin);
consignment.getCheckins().sort(Comparator.comparing(Checkin::getTimeStamp));
if (checkin.getLocation().equals(consignment.getDestination())) {
consignment.setDelivered(true);
}
shippingDao.save(session, consignment);
}
public Consignment view(String consignmentId) {
try (Session session = sessionFactory.openSession()) {
return shippingDao.find(session, consignmentId)
.orElseGet(Consignment::new);
}
}
}

View File

@ -0,0 +1,47 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
shipping-tracker
Sample SAM Template for shipping-tracker
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 20
Resources:
ShippingFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ShippingFunction
Handler: com.baeldung.lambda.shipping.App::handleRequest
Runtime: java8
MemorySize: 512
Environment:
Variables:
DB_URL: jdbc:postgresql://postgres/postgres
DB_USER: postgres
DB_PASSWORD: password
Events:
CreateConsignment:
Type: Api
Properties:
Path: /consignment
Method: post
AddItem:
Type: Api
Properties:
Path: /consignment/{id}/item
Method: post
CheckIn:
Type: Api
Properties:
Path: /consignment/{id}/checkin
Method: post
ViewConsignment:
Type: Api
Properties:
Path: /consignment/{id}
Method: get