diff --git a/src/.gitignore b/src/.gitignore
index db07b3a..6576501 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -3,3 +3,7 @@
# run "dep ensure --vendor-only" to download the dependencies to vendor/ based
# on the Gopkg.{toml,lock} files in that directory.
vendor/
+tamagotchi-service/target
+tamagotchi-service/.idea
+*.iml
+
diff --git a/src/tamagotchi-service/.gitignore b/src/tamagotchi-service/.gitignore
new file mode 100644
index 0000000..5033dcd
--- /dev/null
+++ b/src/tamagotchi-service/.gitignore
@@ -0,0 +1,32 @@
+.mta
+.che
+
+
+/**/node_modules
+/**/target
+/**/dist
+
+/**/.project
+/**/.settings
+/**/.classpath
+/**/.springBeans
+/**/.factorypath
+/**/.idea
+
+// Generated Artifacts
+/**/edmx/
+/**/gen
+/**/src/generated
+/**/dist
+/**/webapp/localService/metadata.xml
+
+
+
+
+// Files and folders generated by WebIDE build and not to be committed
+/uideployer/resources/
+/uideployer/deploymentTemp/
+
+
+/**/package-lock.json
+/**/*.mtar
\ No newline at end of file
diff --git a/src/tamagotchi-service/app.yaml b/src/tamagotchi-service/app.yaml
new file mode 100644
index 0000000..23e61a6
--- /dev/null
+++ b/src/tamagotchi-service/app.yaml
@@ -0,0 +1,8 @@
+runtime: java11
+entrypoint: java -jar target/tamagotchi-service-0.1.0.jar
+inbound_services:
+ - warmup
+automatic_scaling:
+ max_instances: 1
+ min_instances: 1
+
diff --git a/src/tamagotchi-service/pom.xml b/src/tamagotchi-service/pom.xml
new file mode 100644
index 0000000..1800402
--- /dev/null
+++ b/src/tamagotchi-service/pom.xml
@@ -0,0 +1,76 @@
+
+
+ 4.0.0
+
+ com.sap
+ tamagotchi-service
+ 0.1.0
+
+
+ 1.8
+ 1.8
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.6.RELEASE
+
+
+
+
+
+ com.google.cloud
+ libraries-bom
+ 2.9.0
+ pom
+ import
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ com.google.cloud
+ google-cloud-pubsub
+
+
+ com.jayway.jsonpath
+ json-path
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ com.google.appengine
+ appengine-maven-plugin
+ 1.9.77
+
+
+
+
+
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/Application.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/Application.java
new file mode 100644
index 0000000..5c4e423
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/Application.java
@@ -0,0 +1,11 @@
+package com.sap.tamagotchi;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/configuration/ApplicationConfiguration.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/configuration/ApplicationConfiguration.java
new file mode 100644
index 0000000..88c0409
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/configuration/ApplicationConfiguration.java
@@ -0,0 +1,24 @@
+package com.sap.tamagotchi.configuration;
+
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class ApplicationConfiguration {
+
+ @Bean
+ public ObjectMapper objectMapper() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
+ objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+ objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
+ objectMapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
+ return objectMapper;
+ }
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/controller/DeviceController.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/controller/DeviceController.java
new file mode 100644
index 0000000..08a44fc
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/controller/DeviceController.java
@@ -0,0 +1,65 @@
+package com.sap.tamagotchi.controller;
+
+import static com.sap.tamagotchi.model.Color.RED;
+import static com.sap.tamagotchi.model.Color.YELLOW;
+import static org.springframework.http.ResponseEntity.ok;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.sap.tamagotchi.model.Color;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.sap.tamagotchi.model.CreateDevicePayload;
+import com.sap.tamagotchi.model.Device;
+import com.sap.tamagotchi.service.TamagotchiService;
+
+@RestController
+public class DeviceController {
+
+ private final TamagotchiService tamagotchiService;
+
+ @Autowired
+ public DeviceController(TamagotchiService tamagotchiService) {
+ this.tamagotchiService = tamagotchiService;
+ }
+
+ private static Color mapColor(String productId) {
+ switch (productId) {
+ case "66VCHSJNUP":
+ return RED;
+ default:
+ return YELLOW;
+ }
+ }
+
+ @RequestMapping("/devices/{deviceId}")
+ public Device getDevice(String deviceId) {
+ return tamagotchiService.getDevice(deviceId);
+ }
+
+ @RequestMapping("/devices")
+ public Collection getDevices() {
+ return tamagotchiService.getDevices();
+ }
+
+ @PostMapping("/devices")
+ public ResponseEntity createDevice(@RequestBody Collection payload) {
+ List devices = new ArrayList<>();
+ for (CreateDevicePayload p : payload) {
+ devices.add(tamagotchiService.createDevice(new Device(p.getOwner(), mapColor(p.getProductId()))));
+ }
+ return ok(devices);
+ }
+
+ @RequestMapping("/_ah/warmup")
+ public String warmup() {
+ return "warming up";
+ }
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/controller/OwnerController.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/controller/OwnerController.java
new file mode 100644
index 0000000..557cdca
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/controller/OwnerController.java
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2019, SAP SE, All rights reserved.
+ */
+package com.sap.tamagotchi.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.sap.tamagotchi.model.Owner;
+
+@RestController
+@RequestMapping("/owner")
+public class OwnerController {
+
+ @Autowired
+ Owner owner;
+
+ @GetMapping()
+ public void killTamagotchi() {
+ owner.killRendomDevice();
+ }
+
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Care.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Care.java
new file mode 100644
index 0000000..5337c55
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Care.java
@@ -0,0 +1,24 @@
+package com.sap.tamagotchi.model;
+
+public class Care {
+
+ private int feed = 0;
+ private int pet = 0;
+
+ public int getFeed() {
+ return feed;
+ }
+
+ public void setFeed(int feed) {
+ this.feed = feed;
+ }
+
+ public int getPet() {
+ return pet;
+ }
+
+ public void setPet(int pet) {
+ this.pet = pet;
+ }
+
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Color.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Color.java
new file mode 100644
index 0000000..82a9c2f
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Color.java
@@ -0,0 +1,5 @@
+package com.sap.tamagotchi.model;
+
+public enum Color {
+ RED, YELLOW, BLUE, GREEN
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/CreateDevicePayload.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/CreateDevicePayload.java
new file mode 100644
index 0000000..52f9782
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/CreateDevicePayload.java
@@ -0,0 +1,25 @@
+package com.sap.tamagotchi.model;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CreateDevicePayload {
+ private final String userId;
+ private final String productId;
+
+ @JsonCreator
+ public CreateDevicePayload(@JsonProperty("userId") String userId, @JsonProperty("productId") String productId) {
+ this.userId = userId;
+ this.productId = productId;
+ }
+
+ @JsonProperty("userId")
+ public String getOwner() {
+ return userId;
+ }
+
+ @JsonProperty("productId")
+ public String getProductId() {
+ return productId;
+ }
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/DefunctNotification.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/DefunctNotification.java
new file mode 100644
index 0000000..1666c9a
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/DefunctNotification.java
@@ -0,0 +1,25 @@
+package com.sap.tamagotchi.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class DefunctNotification implements IoTMessage {
+
+ @JsonProperty("message")
+ private final String message;
+
+ public DefunctNotification(String message) {
+ this.message = message;
+ }
+
+ @JsonIgnore
+ @Override
+ public String getTopic() {
+ return "tamagotchi-defunct";
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Device.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Device.java
new file mode 100644
index 0000000..58226ca
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Device.java
@@ -0,0 +1,87 @@
+package com.sap.tamagotchi.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.Instant;
+import java.util.Queue;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class Device {
+
+ @JsonProperty("id")
+ private final String id = UUID.randomUUID().toString();
+ @JsonProperty("owner")
+ private final String owner;
+ @JsonProperty("color")
+ private final Color color;
+ @JsonProperty("born")
+ private final Instant born = Instant.now();
+ @JsonProperty("healthScore")
+ private int healthScore = 100;
+ private final Queue messages = new ConcurrentLinkedQueue<>();
+
+ public Device(String owner, Color color) {
+ this.owner = owner;
+ this.color = color;
+ }
+
+ @JsonProperty("id")
+ public String getId() {
+ return id;
+ }
+
+ @JsonProperty("owner")
+ public String getOwner() {
+ return owner;
+ }
+
+ @JsonProperty("color")
+ public Color getColor() {
+ return color;
+ }
+
+ @JsonProperty("born")
+ public String getBorn() {
+ return born.toString();
+ }
+
+ @JsonProperty("healthScore")
+ public int getHealthScore() {
+ return healthScore;
+ }
+
+ @JsonProperty("isAlive")
+ public boolean isAlive() {
+ return healthScore > 1;
+ }
+
+ @JsonIgnore
+ public void changeHealthScore(int delta) {
+ int oldScore = healthScore;
+ if (healthScore >= 1) {
+ healthScore += delta;
+ if (healthScore > 150)
+ healthScore /= 10;
+ }
+
+ if (healthScore < 1) {
+ healthScore = 0;
+ messages.add(new DeviceEvent(id, owner, color, born, healthScore, oldScore, Instant.now()));
+ } else
+ messages.add(new DeviceEvent(id, owner, color, born, healthScore, null, Instant.now()));
+ }
+
+ @JsonIgnore
+ public boolean hasMessages() {
+ return !messages.isEmpty();
+ }
+
+ @JsonIgnore
+ public Queue getMessages() {
+ return messages;
+ }
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/DeviceEvent.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/DeviceEvent.java
new file mode 100644
index 0000000..d757795
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/DeviceEvent.java
@@ -0,0 +1,95 @@
+package com.sap.tamagotchi.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import java.time.Instant;
+
+
+public class DeviceEvent implements IoTMessage {
+
+ @JsonProperty("id")
+ private final String id;
+ @JsonProperty("owner")
+ private final String owner;
+ @JsonProperty("color")
+ private final Color color;
+ @JsonProperty("born")
+ private final Instant born;
+ @JsonProperty("healthScore")
+ private final Integer healthScore;
+ @JsonProperty("lastHealthScore")
+ private final Integer lastHealthScore;
+ @JsonProperty("eventTime")
+ private final Instant eventTime;
+
+ public DeviceEvent(String id, String owner, Color color, Instant born, Integer healthScore, Integer lastHealthScore, Instant eventTime) {
+ this.id = id;
+ this.owner = owner;
+ this.color = color;
+ this.born = born;
+ this.healthScore = healthScore;
+ this.lastHealthScore = lastHealthScore;
+ this.eventTime = eventTime;
+ }
+
+ @JsonProperty("id")
+ public String getId() {
+ return id;
+ }
+
+ @JsonProperty("owner")
+ public String getOwner() {
+ return owner;
+ }
+
+ @JsonProperty("color")
+ public Color getColor() {
+ return color;
+ }
+
+ @JsonProperty("born")
+ public String getBorn() {
+ return born.toString();
+ }
+
+ @JsonProperty("healthScore")
+ public Integer getHealthScore() {
+ return healthScore;
+ }
+
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ @JsonProperty("lastHealthScore")
+ public Integer getLastHealthScore() {
+ return lastHealthScore;
+ }
+
+ @JsonProperty("eventTime")
+ public String getEventTime() {
+ return eventTime.toString();
+ }
+
+ @JsonProperty("isAlive")
+ public boolean isAlive() {
+ return healthScore > 1;
+ }
+
+ @JsonIgnore
+ @Override
+ public String getTopic() {
+ return "tamagotchi-events";
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceEvent{" +
+ "id='" + id + '\'' +
+ ", owner='" + owner + '\'' +
+ ", color=" + color +
+ ", born=" + born +
+ ", healthScore=" + healthScore +
+ ", eventTime=" + eventTime +
+ '}';
+ }
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/IoTMessage.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/IoTMessage.java
new file mode 100644
index 0000000..1de2d9b
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/IoTMessage.java
@@ -0,0 +1,5 @@
+package com.sap.tamagotchi.model;
+
+public interface IoTMessage {
+ String getTopic();
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Owner.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Owner.java
new file mode 100644
index 0000000..4f97ae8
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/model/Owner.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2019, SAP SE, All rights reserved.
+ */
+package com.sap.tamagotchi.model;
+
+import static com.sap.tamagotchi.service.TamagotchiService.DEVICE_EVENT_PROCESSOR_SCHEDULE;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+import com.sap.tamagotchi.service.TamagotchiService;
+
+@Service
+@EnableScheduling
+public class Owner {
+
+ private TamagotchiService tamagotchiService;
+
+ @Autowired
+ public Owner(TamagotchiService tamagotchiService) {
+ this.tamagotchiService = tamagotchiService;
+ }
+
+ @Scheduled(fixedDelay = DEVICE_EVENT_PROCESSOR_SCHEDULE)
+ public void setData() {
+ for (Device d : tamagotchiService.getDevices()) {
+
+ String userName = d.getOwner().substring(0, d.getOwner().indexOf("@")).toUpperCase();
+
+ int careAboutThePet = 0;
+
+ for (int i = 0; i < userName.length(); i++) {
+ if (d.getColor().toString().indexOf(userName.charAt(i)) != -1) {
+ careAboutThePet++;
+ }
+ }
+
+ Care care = new Care();
+
+ if (careAboutThePet == 0) {
+ care.setFeed(-5);
+ } else {
+ care.setFeed(5);
+ }
+ tamagotchiService.takeCare(d.getId(), care);
+ }
+ }
+
+ @Scheduled(fixedDelay = 30000)
+ public void killRendomDevice() {
+
+ Collection devices = tamagotchiService.getDevices();
+
+ if (devices != null && devices.iterator().hasNext()) {
+ Device first = devices.iterator().next();
+ Care care = new Care();
+ care.setFeed(-100000);
+ tamagotchiService.takeCare(first.getId(), care);
+ }
+ }
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/publisher/PublisherService.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/publisher/PublisherService.java
new file mode 100644
index 0000000..fd9724a
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/publisher/PublisherService.java
@@ -0,0 +1,82 @@
+package com.sap.tamagotchi.publisher;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutures;
+import com.google.cloud.ServiceOptions;
+import com.google.cloud.pubsub.v1.Publisher;
+import com.google.protobuf.ByteString;
+import com.google.pubsub.v1.ProjectTopicName;
+import com.google.pubsub.v1.PubsubMessage;
+import com.sap.tamagotchi.model.IoTMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.List;
+
+@Service
+public class PublisherService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ // use the default project id
+ private static final String PROJECT_ID = ServiceOptions.getDefaultProjectId();
+ private final ObjectMapper mapper;
+
+ @Autowired
+ public PublisherService(ObjectMapper mapper) {
+ this.mapper = mapper;
+ }
+
+ public void publish(IoTMessage message) throws Exception {
+ if (message == null) {
+ LOGGER.info("received null message");
+ return;
+ }
+ String topicId = message.getTopic();
+ ProjectTopicName topicName = ProjectTopicName.of(PROJECT_ID, topicId);
+ Publisher publisher = null;
+ List> futures = new ArrayList<>();
+
+ try {
+ String stringMessage = mapper.writeValueAsString(message);
+ // Create a publisher instance with default settings bound to the topic
+ publisher = Publisher.newBuilder(topicName).build();
+ LOGGER.info("publish to topic" + publisher.getTopicNameString());
+
+ // convert message to bytes
+ ByteString data = ByteString.copyFromUtf8(stringMessage);
+ PubsubMessage pubsubMessage = PubsubMessage.newBuilder()
+ .setData(data)
+ .build();
+
+ LOGGER.info("publish to message" + stringMessage);
+
+ // Schedule a message to be published. Messages are automatically batched.
+ ApiFuture future = publisher.publish(pubsubMessage);
+ futures.add(future);
+
+ } finally {
+ // Wait on any pending requests
+ List messageIds = ApiFutures.allAsList(futures).get();
+
+ for (String messageId : messageIds) {
+ System.out.println(messageId);
+ LOGGER.info("publish successful : " + messageId);
+ }
+
+ if (publisher != null) {
+ // When finished with the publisher, shutdown to free up resources.
+ publisher.shutdown();
+ }
+
+ if (messageIds.isEmpty())
+ LOGGER.info("no messages published ");
+ }
+ }
+}
diff --git a/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/service/TamagotchiService.java b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/service/TamagotchiService.java
new file mode 100644
index 0000000..5f609de
--- /dev/null
+++ b/src/tamagotchi-service/src/main/java/com/sap/tamagotchi/service/TamagotchiService.java
@@ -0,0 +1,109 @@
+package com.sap.tamagotchi.service;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import com.sap.tamagotchi.model.Care;
+import com.sap.tamagotchi.model.DefunctNotification;
+import com.sap.tamagotchi.model.Device;
+import com.sap.tamagotchi.publisher.PublisherService;
+
+@Service
+@EnableScheduling
+public class TamagotchiService {
+
+ public static final long DEVICE_EVENT_PROCESSOR_SCHEDULE = 5000;
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static final Map deviceRegistry = Collections.synchronizedMap(new HashMap<>());
+
+ private final PublisherService publisherService;
+
+ @Autowired
+ public TamagotchiService(PublisherService publisherService) {
+ this.publisherService = publisherService;
+ }
+
+ public Device getDevice(String deviceId) {
+ return deviceRegistry.get(deviceId);
+ }
+
+ public Collection getDevices() {
+ return deviceRegistry.values();
+ }
+
+ public Set getDeviceIds() {
+ return deviceRegistry.keySet();
+ }
+
+ public Device createDevice(Device device) {
+ deviceRegistry.put(device.getId(), device);
+ return device;
+ }
+
+ public void takeCare(String deviceId, Care care) {
+ Device device = deviceRegistry.get(deviceId);
+ if (device == null) {
+ return;
+ }
+ device.changeHealthScore(care.getFeed());
+ device.changeHealthScore(care.getPet());
+ }
+
+ @Scheduled(fixedDelay = DEVICE_EVENT_PROCESSOR_SCHEDULE)
+ private void processDeviceEvents() {
+ deviceRegistry
+ .values()
+ .parallelStream()
+ .filter(Device::hasMessages)
+ .forEach(device -> {
+ while (device.getMessages().peek() != null) {
+ try {
+ publisherService.publish(device.getMessages().poll());
+ } catch (Exception ex) {
+ LOGGER.error("processing device events failed: {}", ex.getMessage());
+ }
+ }
+ });
+
+ // remove dead devices
+ deviceRegistry
+ .values()
+ .parallelStream()
+ .filter(device -> !device.isAlive())
+ .forEach(device -> {
+ sendTamagotchiDefunctNotifiction(device.getId());
+ deviceRegistry.remove(device.getId());
+ LOGGER.info("{} has died", device.getId());
+ });
+ }
+
+ private void sendTamagotchiDefunctNotifiction(String id) {
+
+ Device device = deviceRegistry.get(id);
+ if (device == null || device.getId() == null || device.getOwner() == null) {
+ return;
+ }
+ String defunctMessage = String.format("Tamagotchi %s of %s passed away", device.getId(), device.getOwner());
+ DefunctNotification defunctNotification = new DefunctNotification(defunctMessage);
+ try {
+ publisherService.publish(defunctNotification);
+ LOGGER.info("defunct notification sent for {}", device.getId());
+ } catch (Exception ex) {
+ LOGGER.error("sendTamagotchiDefunctNotifiction failed: {}", ex.getMessage());
+ }
+ }
+
+}
diff --git a/src/tamagotchi-service/src/test/java/com/sap/tamagotchi/model/OwnerTest.java b/src/tamagotchi-service/src/test/java/com/sap/tamagotchi/model/OwnerTest.java
new file mode 100644
index 0000000..35e2820
--- /dev/null
+++ b/src/tamagotchi-service/src/test/java/com/sap/tamagotchi/model/OwnerTest.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2019, SAP SE, All rights reserved.
+ */
+package com.sap.tamagotchi.model;
+
+import static java.util.Arrays.asList;
+import static org.mockito.Mockito.when;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.junit4.SpringRunner;
+import com.sap.tamagotchi.service.TamagotchiService;;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class OwnerTest {
+
+ @Autowired
+ Owner owner;
+
+ @MockBean
+ TamagotchiService tamagotchiService;
+
+ Collection mockDevices;
+
+ // @Before
+ // public void init() {
+ // MockitoAnnotations.initMocks(this);
+ // }
+
+ @Test
+ public void testIt() {
+ when(tamagotchiService.getDevices()).thenReturn(asList(new Device("elisa@sap.com", Color.BLUE)));
+ owner.setData();
+
+ }
+}