From 19f578535079ecb9850c3d8c56e7d7a8234a94ed Mon Sep 17 00:00:00 2001 From: John Blum Date: Fri, 21 Sep 2018 00:43:11 -0700 Subject: [PATCH] Add sample demonstrating the use of Spring Boot Actuator with Apache Geode. --- .../spring-geode-sample-boot-actuator.gradle | 22 +++++ .../temp/event/BoilingTemperatureEvent.java | 27 ++++++ .../temp/event/FreezingTemperatureEvent.java | 27 ++++++ .../app/temp/event/TemperatureEvent.java | 35 ++++++++ .../client/BootGeodeClientApplication.java | 58 +++++++++++++ .../server/BootGeodeServerApplication.java | 83 +++++++++++++++++++ .../app/temp/model/TemperatureReading.java | 67 +++++++++++++++ .../repo/TemperatureReadingRepository.java | 38 +++++++++ .../app/temp/service/TemperatureMonitor.java | 67 +++++++++++++++ .../app/temp/service/TemperatureSensor.java | 59 +++++++++++++ .../resources/application-client.properties | 1 + .../resources/application-server.properties | 1 + .../src/main/resources/application.properties | 3 + 13 files changed, 488 insertions(+) create mode 100644 samples/boot/actuator/spring-geode-sample-boot-actuator.gradle create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/event/BoilingTemperatureEvent.java create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/event/FreezingTemperatureEvent.java create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/event/TemperatureEvent.java create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/geode/client/BootGeodeClientApplication.java create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/geode/server/BootGeodeServerApplication.java create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/model/TemperatureReading.java create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/repo/TemperatureReadingRepository.java create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/service/TemperatureMonitor.java create mode 100644 samples/boot/actuator/src/main/java/example/app/temp/service/TemperatureSensor.java create mode 100644 samples/boot/actuator/src/main/resources/application-client.properties create mode 100644 samples/boot/actuator/src/main/resources/application-server.properties create mode 100644 samples/boot/actuator/src/main/resources/application.properties diff --git a/samples/boot/actuator/spring-geode-sample-boot-actuator.gradle b/samples/boot/actuator/spring-geode-sample-boot-actuator.gradle new file mode 100644 index 00000000..710449d6 --- /dev/null +++ b/samples/boot/actuator/spring-geode-sample-boot-actuator.gradle @@ -0,0 +1,22 @@ +apply plugin: 'io.spring.convention.spring-sample-boot' + +description = "Spring Geode Sample demonstrating the use of Spring Boot Actuator with Apache Geode." + +dependencies { + + compile project(":spring-geode-starter") + compile project(":spring-geode-actuator-autoconfigure") + + compile ("org.springframework.boot:spring-boot-starter-web") { + exclude group: "org.springframework.boot", module: "spring-boot-starter-logging" + } + + compile "org.springframework.data:spring-data-geode-test" + + runtime "org.springframework.shell:spring-shell" + + runtime ("org.springframework.boot:spring-boot-starter-jetty") { + exclude group: "org.springframework.boot", module: "spring-boot-starter-logging" + } + +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/event/BoilingTemperatureEvent.java b/samples/boot/actuator/src/main/java/example/app/temp/event/BoilingTemperatureEvent.java new file mode 100644 index 00000000..b062f181 --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/event/BoilingTemperatureEvent.java @@ -0,0 +1,27 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.event; + +import example.app.temp.model.TemperatureReading; + +@SuppressWarnings("unused") +public class BoilingTemperatureEvent extends TemperatureEvent { + + public BoilingTemperatureEvent(Object source, TemperatureReading temperatureReading) { + super(source, temperatureReading); + } +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/event/FreezingTemperatureEvent.java b/samples/boot/actuator/src/main/java/example/app/temp/event/FreezingTemperatureEvent.java new file mode 100644 index 00000000..1229c919 --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/event/FreezingTemperatureEvent.java @@ -0,0 +1,27 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.event; + +import example.app.temp.model.TemperatureReading; + +@SuppressWarnings("unused") +public class FreezingTemperatureEvent extends TemperatureEvent { + + public FreezingTemperatureEvent(Object source, TemperatureReading temperatureReading) { + super(source, temperatureReading); + } +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/event/TemperatureEvent.java b/samples/boot/actuator/src/main/java/example/app/temp/event/TemperatureEvent.java new file mode 100644 index 00000000..ea5b4088 --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/event/TemperatureEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.event; + +import org.springframework.context.ApplicationEvent; + +import example.app.temp.model.TemperatureReading; +import lombok.Getter; + +public class TemperatureEvent extends ApplicationEvent { + + @Getter + private final TemperatureReading temperatureReading; + + public TemperatureEvent(Object source, TemperatureReading temperatureReading) { + + super(source); + + this.temperatureReading = temperatureReading; + } +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/geode/client/BootGeodeClientApplication.java b/samples/boot/actuator/src/main/java/example/app/temp/geode/client/BootGeodeClientApplication.java new file mode 100644 index 00000000..1fe93a5e --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/geode/client/BootGeodeClientApplication.java @@ -0,0 +1,58 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.geode.client; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.event.EventListener; +import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions; + +import example.app.temp.event.BoilingTemperatureEvent; +import example.app.temp.event.FreezingTemperatureEvent; +import example.app.temp.event.TemperatureEvent; +import example.app.temp.model.TemperatureReading; +import example.app.temp.service.TemperatureMonitor; + +@SpringBootApplication +@EnableEntityDefinedRegions(basePackageClasses = TemperatureReading.class) +@SuppressWarnings("unused") +public class BootGeodeClientApplication { + + public static void main(String[] args) { + + new SpringApplicationBuilder(BootGeodeClientApplication.class) + .web(WebApplicationType.SERVLET) + .build() + .run(args); + } + + @Bean + TemperatureMonitor temperatureMonitor(ApplicationEventPublisher applicationEventPublisher) { + return new TemperatureMonitor(applicationEventPublisher); + } + + @EventListener(classes = { BoilingTemperatureEvent.class, FreezingTemperatureEvent.class }) + public void temperatureEventHandler(TemperatureEvent temperatureEvent) { + + System.err.printf("%1$s TEMPERATURE READING [%2$s]%n", + temperatureEvent instanceof BoilingTemperatureEvent ? "HOT" : "COLD", + temperatureEvent.getTemperatureReading()); + } +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/geode/server/BootGeodeServerApplication.java b/samples/boot/actuator/src/main/java/example/app/temp/geode/server/BootGeodeServerApplication.java new file mode 100644 index 00000000..a768ce81 --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/geode/server/BootGeodeServerApplication.java @@ -0,0 +1,83 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.geode.server; + +import org.apache.geode.cache.GemFireCache; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.DependsOn; +import org.springframework.data.gemfire.IndexFactoryBean; +import org.springframework.data.gemfire.config.annotation.CacheServerApplication; +import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions; +import org.springframework.data.gemfire.repository.config.EnableGemfireRepositories; +import org.springframework.scheduling.annotation.EnableScheduling; + +import example.app.temp.model.TemperatureReading; +import example.app.temp.repo.TemperatureReadingRepository; +import example.app.temp.service.TemperatureSensor; + +@SpringBootApplication +@CacheServerApplication(name = "TemperatureServiceServer") +@EnableEntityDefinedRegions(basePackageClasses = TemperatureReading.class) +@EnableGemfireRepositories(basePackageClasses = TemperatureReadingRepository.class) +@EnableScheduling +@SuppressWarnings("unused") +public class BootGeodeServerApplication { + + public static void main(String[] args) { + + new SpringApplicationBuilder(BootGeodeServerApplication.class) + .web(WebApplicationType.SERVLET) + .build() + .run(args); + } + + @Bean + TemperatureSensor temperatureSensor(TemperatureReadingRepository repository) { + return new TemperatureSensor(repository); + } + + @Bean + @DependsOn("TemperatureReadings") + IndexFactoryBean temperatureReadingTemperatureIndex(GemFireCache gemfireCache) { + + IndexFactoryBean temperatureTimestampIndex = new IndexFactoryBean(); + + temperatureTimestampIndex.setCache(gemfireCache); + temperatureTimestampIndex.setExpression("temperature"); + temperatureTimestampIndex.setFrom("/TemperatureReadings"); + temperatureTimestampIndex.setName("TemperatureReadingTemperatureIdx"); + + return temperatureTimestampIndex; + } + + @Bean + @DependsOn("TemperatureReadings") + IndexFactoryBean temperatureReadingTimestampIndex(GemFireCache gemfireCache) { + + IndexFactoryBean temperatureTimestampIndex = new IndexFactoryBean(); + + temperatureTimestampIndex.setCache(gemfireCache); + temperatureTimestampIndex.setExpression("timestamp"); + temperatureTimestampIndex.setFrom("/TemperatureReadings"); + temperatureTimestampIndex.setName("TemperatureReadingTimestampIdx"); + + return temperatureTimestampIndex; + } +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/model/TemperatureReading.java b/samples/boot/actuator/src/main/java/example/app/temp/model/TemperatureReading.java new file mode 100644 index 00000000..2e8d2d47 --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/model/TemperatureReading.java @@ -0,0 +1,67 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.model; + +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; +import org.springframework.data.gemfire.mapping.annotation.Region; + +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; + +@Data +@Region("TemperatureReadings") +@RequiredArgsConstructor(staticName = "newTemperatureReading") +@SuppressWarnings("unused") +public class TemperatureReading { + + private static final int BOILING_TEMPERATURE = 212; + private static final int FREEZING_TEMPERATURE = 32; + + @Id + private Long timestamp = System.currentTimeMillis(); + + @NonNull + private Integer temperature; + + @Transient + public boolean isBoiling() { + + Integer temperature = getTemperature(); + + return temperature != null && temperature >= BOILING_TEMPERATURE; + } + + @Transient + public boolean isNormal() { + return !(isBoiling() || isFreezing()); + } + + @Transient + public boolean isFreezing() { + + Integer temperature = getTemperature(); + + return temperature != null && temperature <= FREEZING_TEMPERATURE; + } + + @Override + public String toString() { + return String.format("%d °F", getTemperature()); + } +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/repo/TemperatureReadingRepository.java b/samples/boot/actuator/src/main/java/example/app/temp/repo/TemperatureReadingRepository.java new file mode 100644 index 00000000..e20015df --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/repo/TemperatureReadingRepository.java @@ -0,0 +1,38 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.repo; + +import java.util.List; + +import org.springframework.data.gemfire.repository.Query; +import org.springframework.data.repository.CrudRepository; + +import example.app.temp.model.TemperatureReading; + +@SuppressWarnings("unused") +public interface TemperatureReadingRepository extends CrudRepository { + + List findByTimestampGreaterThanAndTimestampLessThan(Long timestampLowerBound, + Long timestampUpperBound); + + @Query("SELECT count(*) FROM /TemperatureReadings WHERE temperature >= 212") + Integer countBoilingTemperatureReadings(); + + @Query("SELECT count(*) FROM /TemperatureReadings WHERE temperature <= 32") + Integer countFreezingTemperatureReadings(); + +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/service/TemperatureMonitor.java b/samples/boot/actuator/src/main/java/example/app/temp/service/TemperatureMonitor.java new file mode 100644 index 00000000..1d0703f7 --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/service/TemperatureMonitor.java @@ -0,0 +1,67 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.service; + +import java.util.Optional; + +import org.apache.geode.cache.query.CqEvent; +import org.apache.shiro.util.Assert; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.gemfire.listener.annotation.ContinuousQuery; +import org.springframework.stereotype.Service; + +import example.app.temp.event.BoilingTemperatureEvent; +import example.app.temp.event.FreezingTemperatureEvent; +import example.app.temp.model.TemperatureReading; + +@Service +@SuppressWarnings("unused") +public class TemperatureMonitor { + + private final ApplicationEventPublisher applicationEventPublisher; + + public TemperatureMonitor(ApplicationEventPublisher applicationEventPublisher) { + + Assert.notNull(applicationEventPublisher, "ApplicationEventPublisher is required"); + + this.applicationEventPublisher = applicationEventPublisher; + } + + @ContinuousQuery(name = "BoilingTemperatureMonitor", + query = "SELECT * FROM /TemperatureReadings WHERE temperature >= 212") + public void boilingTemperatureReadings(CqEvent event) { + + Optional.ofNullable(event) + .map(CqEvent::getNewValue) + .filter(TemperatureReading.class::isInstance) + .map(TemperatureReading.class::cast) + .map(it -> new BoilingTemperatureEvent(this, it)) + .ifPresent(this.applicationEventPublisher::publishEvent); + } + + @ContinuousQuery(name = "FreezingTemperatureMonitor", + query = "SELECT * FROM /TemperatureReadings WHERE temperature <= 32") + public void freezingTemperatureReadings(CqEvent event) { + + Optional.ofNullable(event) + .map(CqEvent::getNewValue) + .filter(TemperatureReading.class::isInstance) + .map(TemperatureReading.class::cast) + .map(it -> new FreezingTemperatureEvent(this, it)) + .ifPresent(this.applicationEventPublisher::publishEvent); + } +} diff --git a/samples/boot/actuator/src/main/java/example/app/temp/service/TemperatureSensor.java b/samples/boot/actuator/src/main/java/example/app/temp/service/TemperatureSensor.java new file mode 100644 index 00000000..060d95d0 --- /dev/null +++ b/samples/boot/actuator/src/main/java/example/app/temp/service/TemperatureSensor.java @@ -0,0 +1,59 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package example.app.temp.service; + +import java.io.PrintStream; +import java.util.PrimitiveIterator; +import java.util.Random; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import example.app.temp.model.TemperatureReading; +import example.app.temp.repo.TemperatureReadingRepository; + +@Service +@SuppressWarnings("unused") +public class TemperatureSensor { + + private final PrimitiveIterator.OfInt temperatureStream = new Random(System.currentTimeMillis()) + .ints(-100, 400).iterator(); + + private final TemperatureReadingRepository repository; + + public TemperatureSensor(TemperatureReadingRepository repository) { + + Assert.notNull(repository, "TemperatureReadingRepository is required"); + + this.repository = repository; + } + + @Scheduled(fixedRateString = "${example.app.temp.sensor.reading.schedule.rate:1000}") + public void readTemperature() { + this.repository.save(log(TemperatureReading.newTemperatureReading(temperatureStream.nextInt()))); + } + + private TemperatureReading log(TemperatureReading temperatureReading) { + + PrintStream out = temperatureReading.isNormal() ? System.out : System.err; + + out.printf("TEMPERATURE READING [%s]%n", temperatureReading); + + return temperatureReading; + } +} diff --git a/samples/boot/actuator/src/main/resources/application-client.properties b/samples/boot/actuator/src/main/resources/application-client.properties new file mode 100644 index 00000000..7df0e21c --- /dev/null +++ b/samples/boot/actuator/src/main/resources/application-client.properties @@ -0,0 +1 @@ +server.port=9191 diff --git a/samples/boot/actuator/src/main/resources/application-server.properties b/samples/boot/actuator/src/main/resources/application-server.properties new file mode 100644 index 00000000..d636638d --- /dev/null +++ b/samples/boot/actuator/src/main/resources/application-server.properties @@ -0,0 +1 @@ +server.port=8181 diff --git a/samples/boot/actuator/src/main/resources/application.properties b/samples/boot/actuator/src/main/resources/application.properties new file mode 100644 index 00000000..29c615a7 --- /dev/null +++ b/samples/boot/actuator/src/main/resources/application.properties @@ -0,0 +1,3 @@ +management.endpoint.health.show-details=always +#management.endpoints.web.exposure.include=env +spring.data.gemfire.cache.log-level=error