Skip to content
This repository has been archived by the owner on Jul 26, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into feature/customer-api-consumer
Browse files Browse the repository at this point in the history
  • Loading branch information
Ralf Ueberfuhr committed Jun 21, 2024
2 parents 107866b + 63f0411 commit 3d9bfc2
Show file tree
Hide file tree
Showing 19 changed files with 682 additions and 2 deletions.
12 changes: 12 additions & 0 deletions customer-api-provider/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package de.schulung.sample.quarkus.infrastructure;

import io.quarkus.runtime.Startup;
import io.smallrye.mutiny.Uni;
import io.smallrye.mutiny.infrastructure.Infrastructure;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;
import org.eclipse.microprofile.health.Readiness;

@ApplicationScoped
public class LongRunningStartup {

private volatile boolean initialized = false;
private volatile boolean initializingFailed = false;

// check during startup: http://localhost:9090/q/health/ready

private Uni<Void> doLongRunningInitialization() {
try {
Thread.sleep(5000);
initialized = true;
return Uni.createFrom().voidItem();
} catch (InterruptedException e) {
initializingFailed = true;
return Uni.createFrom().failure(e);
}
}

@Startup
public void init() {
Uni
.createFrom()
.voidItem()
.emitOn(Infrastructure.getDefaultWorkerPool())
.subscribe()
.with(v -> this.doLongRunningInitialization());
}

@Produces
@ApplicationScoped
@Liveness
public HealthCheck myStartupLiveness() {
return () -> {
final var result = HealthCheckResponse
.named("My long-running startup");
return (initializingFailed ? result.down() : result.up()).build();
};
}

@Produces
@ApplicationScoped
@Readiness
public HealthCheck myStartupReadyness() {
return () -> {
final var result = HealthCheckResponse
.named("My long-running startup");
return (initialized ? result.up() : result.down()).build();
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.schulung.sample.quarkus.persistence;

import de.schulung.sample.quarkus.domain.Customer;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDate;
import java.util.UUID;

@Getter
@Setter
@Entity(name = "Customer")
@Table(name = "CUSTOMERS")
public class CustomerEntity {

@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID uuid;
@Size(min = 3, max = 100)
@NotNull
private String name;
@Column(name = "DATE_OF_BIRTH")
private LocalDate birthdate;
@NotNull
//@Enumerated(EnumType.STRING)
private Customer.CustomerState state = Customer.CustomerState.ACTIVE;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package de.schulung.sample.quarkus.persistence;

import de.schulung.sample.quarkus.domain.Customer;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;

@Mapper(componentModel = "cdi")
public interface CustomerEntityMapper {

CustomerEntity map(Customer source);

Customer map(CustomerEntity source);

void copy(CustomerEntity source, @MappingTarget Customer target);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package de.schulung.sample.quarkus.persistence;

import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
import jakarta.enterprise.context.ApplicationScoped;

import java.util.UUID;

@ApplicationScoped
public class CustomerEntityRepository implements PanacheRepositoryBase<CustomerEntity, UUID> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package de.schulung.sample.quarkus.persistence;

import de.schulung.sample.quarkus.domain.Customer;
import de.schulung.sample.quarkus.domain.Customer.CustomerState;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import jakarta.persistence.PersistenceException;

@Converter(autoApply = true)
public class CustomerStateConverter implements AttributeConverter<CustomerState, String> {


@Override
public String convertToDatabaseColumn(CustomerState source) {
return null == source ? null : switch (source) {
case ACTIVE -> "a";
case LOCKED -> "l";
case DISABLED -> "d";
};
}

@Override
public CustomerState convertToEntityAttribute(String source) {
return null == source ? null : switch (source) {
case "a" -> Customer.CustomerState.ACTIVE;
case "l" -> Customer.CustomerState.LOCKED;
case "d" -> Customer.CustomerState.DISABLED;
default -> throw new PersistenceException(source + " is not allowed here.");
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package de.schulung.sample.quarkus.persistence;

import de.schulung.sample.quarkus.domain.Customer;
import de.schulung.sample.quarkus.domain.Customer.CustomerState;
import de.schulung.sample.quarkus.domain.CustomersSink;
import io.quarkus.arc.properties.IfBuildProperty;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Typed;
import lombok.RequiredArgsConstructor;

import javax.sql.DataSource;
import java.sql.*;
import java.time.LocalDate;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;

@ApplicationScoped
@Typed(CustomersSink.class)
@RequiredArgsConstructor
@IfBuildProperty(
name = "persistence.sink.implementation",
stringValue = "jdbc"
)
public class CustomersSinkJdbcImpl implements CustomersSink {

private final DataSource ds;

/* ******************************************************* *
* Converter methods - could be converter objects instead *
* ******************************************************* */

private static UUID convertUuid(String uuid) {
return Optional.ofNullable(uuid)
.map(UUID::fromString)
.orElse(null);
}

private static String convertUuid(UUID uuid) {
return Optional.ofNullable(uuid)
.map(UUID::toString)
.orElse(null);
}

private static LocalDate convertDate(Date date) {
return Optional.ofNullable(date)
.map(Date::toLocalDate)
.orElse(null);
}

private static Date convertDate(LocalDate date) {
return Optional.ofNullable(date)
.map(Date::valueOf)
.orElse(null);
}

private static CustomerState convertState(int value) {
return CustomerState.values()[value];
}

private static int convertState(CustomerState value) {
return Optional.ofNullable(value)
.map(CustomerState::ordinal)
.orElse(0);

}

/* ******************************************************* *
* Row Mapping - could be a RowMapper object instead *
* ******************************************************* */

private static Customer readSingle(ResultSet rs) throws SQLException {
return Customer.builder()
.uuid(convertUuid(rs.getString("UUID")))
.birthdate(convertDate(rs.getDate("DATE_OF_BIRTH")))
.name(rs.getString("NAME"))
.state(convertState(rs.getInt("STATE")))
.build();
}

private static Stream<Customer> readAll(ResultSet rs) throws SQLException {
Collection<Customer> result = new LinkedList<>();
while (rs.next()) {
result.add(readSingle(rs));
}
return result.stream();
}

/* ******************************************************* *
* CustomersSink implementation *
* ******************************************************* */

@Override
public Stream<Customer> findAll() {
try (Connection con = ds.getConnection();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"select * from CUSTOMERS"
)) {

return readAll(rs);

} catch (SQLException e) {
throw new RuntimeException(e); // eigene Exception?
}
}

@Override
public Stream<Customer> findByState(CustomerState state) {
try (Connection con = ds.getConnection();
PreparedStatement stmt = con.prepareStatement(
"select * from CUSTOMERS where STATE=?"
)) {

stmt.setInt(1, convertState(state));

try (ResultSet rs = stmt.executeQuery()) {
return readAll(rs);
}

} catch (SQLException e) {
throw new RuntimeException(e); // eigene Exception?
}
}

@Override
public Optional<Customer> findByUuid(UUID uuid) {
try (Connection con = ds.getConnection();
PreparedStatement stmt = con.prepareStatement(
"select * from CUSTOMERS where UUID=?"
)) {

stmt.setString(1, convertUuid(uuid));

try (ResultSet rs = stmt.executeQuery()) {
if (!rs.next()) {
return Optional.empty();
}
return Optional.of(readSingle(rs));
}

} catch (SQLException e) {
throw new RuntimeException(e); // eigene Exception?
}
}

@Override
public void save(Customer customer) {
// TODO only insert, we need an update too, if the UUID is already set
try (Connection con = ds.getConnection();
PreparedStatement stmt = con.prepareStatement(
"insert into CUSTOMERS(NAME,DATE_OF_BIRTH,STATE) values(?,?,?)",
Statement.RETURN_GENERATED_KEYS
)) {

stmt.setString(1, customer.getName());
stmt.setDate(2, convertDate(customer.getBirthdate()));
stmt.setInt(3, convertState(customer.getState()));
stmt.executeUpdate();

try (ResultSet rs = stmt.getGeneratedKeys()) {
if (!rs.next()) {
throw new RuntimeException("not expected"); // bessere Exception
}
customer.setUuid(convertUuid(rs.getString(1)));
}

} catch (SQLException e) {
throw new RuntimeException(e); // eigene Exception?
}
}

@Override
public boolean delete(UUID uuid) {
try (Connection con = ds.getConnection();
PreparedStatement stmt = con.prepareStatement(
"delete from CUSTOMERS where UUID=?"
)) {

stmt.setString(1, convertUuid(uuid));
return stmt.executeUpdate() > 0;

} catch (SQLException e) {
throw new RuntimeException(e); // eigene Exception?
}
}

@Override
public long count() {
try (Connection con = ds.getConnection();
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(
"select count(uuid) from CUSTOMERS"
)) {

if (!rs.next()) {
return 0;
}
return rs.getLong(1);

} catch (SQLException e) {
throw new RuntimeException(e); // eigene Exception?
}
}
}
Loading

0 comments on commit 3d9bfc2

Please sign in to comment.