├── docker-compose.yml
├── gen-data.sh
├── outline.txt
├── pom.xml
├── run.sh
├── server.jks
├── sonarqube.sh
└── src
├── main
├── docker
│ └── Dockerfile
├── java
│ └── demo
│ │ ├── DemoApplication.java
│ │ ├── Product.java
│ │ └── ProductRepository.java
└── resources
│ ├── application.properties
│ └── logback.xml
└── test
└── java
└── demo
└── DemoApplicationTests.java
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | graphite:
2 | image: hopsoft/graphite-statsd
3 | ports:
4 | - "80:80"
5 | - "2003:2003"
6 | - "8125:8125/udp"
7 |
8 | logstash:
9 | image: pblittle/docker-logstash
10 | environment:
11 | - LOGSTASH_CONFIG_URL=https://raw.githubusercontent.com/mstine/statsd-graphite-demo/master/logstash.conf
12 | ports:
13 | - "10042:10042"
14 | - "9292:9292"
15 | - "9200:9200"
--------------------------------------------------------------------------------
/gen-data.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | seq 1000 | while read l ; do
6 | curl -H "Content-Type: application/json" -d "{\"sku\":\"sku1${l}\"}" http://localhost:8080/products ;
7 | s=`gshuf -i 1-10 -n 1`
8 | echo
9 | echo 0.$s
10 | sleep 0.$s
11 | done
12 |
--------------------------------------------------------------------------------
/outline.txt:
--------------------------------------------------------------------------------
1 | # The Operationalized Application
2 |
3 | * start.spring.io and choose the 1.3 bits (demo curl!)
4 | * build a simple JPA repository
5 | * export it as a REST endpoint w/ annotations
6 | * change the server to Jetty
7 | * stand up the Spring Boot Actuator bits
8 | * move management to diff context-path
9 | * make the Health endpoint respond to failure modes in the application
10 | * git commit ID in the /info endpoint
11 |
12 |
13 | pl.project13.maven
14 | git-commit-id-plugin
15 |
16 |
17 | TODO RUN mvn clean install !!!!
18 | and
19 |
20 |
21 |
22 | src/main/resources
23 | true
24 |
25 |
26 |
27 | info.build.artifact=${project.artifactId}
28 | info.build.version=${project.version}
29 |
30 |
31 |
32 | * org.springframework.boot:spring-boot-actuator-docs &&& endpoints.hal.path=/hal && add org.webjars:hal-browser:*
33 | * collect custom business metrics using the counter/meter service
34 | * expose a custom REST endpoint that can be instrumented using @RepositoryEventHandler and @HandlerAfter*(T t)
35 | * customize the actuator endpoints and use a custom counterService in the custom
36 | REST endpoint and the Spring Data REST endpoint
37 | * configure custom reporters / writers using DW Metrics and raw Actuator: io.dropwizard.metrics:metrics-graphite && com.timgroup:java-statsd-client
38 | * docker-compose up
39 | * what about stuff you can't monitor in terms of numbers: meters, counters, gauges, etc.
40 | * logback.xml: net.logstash.logback:¸logstash-logback-encoder : 4.2
41 | private static Logger LOGGER = LoggerFactory.getLogger( ProductApplication.class);
42 | LogstashMarker logstashMarker = Markers.append( "event", "products." + k)
43 | LOGGER.info(logstashMarker, "products." + k);
44 | * server.ssl.*
45 | * write out a PID file
46 | * run the executable jar.
47 | * run the executable jar by delegating to -D, environment variables, etc.
48 | * run the executable _executable_ jar.
49 | * make the jar a Docker friendly thing.
50 |
51 |
52 |
53 |
54 |
55 | #!/bin/bash
56 |
57 | set -e
58 |
59 | seq 1000 | while read l ; do
60 | curl -H "Content-Type: application/json" -d '{"sku":"sku1$l"}' http://localhost:8080/products ;
61 | s=`gshuf -i 1-10 -n 1`
62 | echo 0.$s
63 | sleep 0.$s
64 | done
65 |
66 |
67 |
68 | http://peter-on-java.blogspot.com/2013/12/importing-ssl-certificates-to-keystore.html
69 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | org.test
7 | product-service
8 | 0.0.1-SNAPSHOT
9 | jar
10 |
11 |
12 |
13 | org.springframework.boot
14 | spring-boot-starter-parent
15 | 1.3.0.RELEASE
16 |
17 |
18 |
19 |
20 |
21 | starbuxman
22 | UTF-8
23 | 1.8
24 |
25 |
26 |
27 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-actuator
39 |
40 |
41 | org.springframework.boot
42 | spring-boot-starter-data-jpa
43 |
44 |
45 | org.springframework.boot
46 | spring-boot-starter-data-rest
47 |
48 |
49 | org.springframework.boot
50 | spring-boot-starter-remote-shell
51 |
52 |
53 | org.springframework.boot
54 | spring-boot-starter-web
55 |
56 |
57 |
58 | com.h2database
59 | h2
60 | runtime
61 |
62 |
63 | org.springframework.boot
64 | spring-boot-starter-test
65 | test
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-actuator-docs
70 |
71 |
75 |
76 | org.springframework.data
77 | spring-data-rest-hal-browser
78 |
79 |
80 |
81 | net.logstash.logback
82 | logstash-logback-encoder
83 | 4.2
84 |
85 |
86 | io.dropwizard.metrics
87 | metrics-graphite
88 |
89 |
90 | com.timgroup
91 | java-statsd-client
92 |
93 |
94 |
95 |
96 | ${project.artifactId}
97 |
98 |
99 | src/main/resources
100 | true
101 |
102 |
103 |
104 |
105 | pl.project13.maven
106 | git-commit-id-plugin
107 |
108 |
109 | org.springframework.boot
110 | spring-boot-maven-plugin
111 |
112 | true
113 |
114 |
115 |
116 |
117 |
118 | com.spotify
119 | docker-maven-plugin
120 | 0.2.3
121 |
122 |
123 |
124 | install
125 |
126 | build
127 |
128 |
129 |
130 |
131 |
132 | ${docker.image.prefix}/${project.artifactId}
133 | src/main/docker
134 |
135 |
136 | /
137 | ${project.build.directory}
138 | ${project.build.finalName}.jar
139 |
140 |
141 | /
142 | ${project.build.directory}
143 | classes/server.jks
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | docker run -e STATSD_HOST=$STATSD_HOST -e STATSD_PORT=$STATSD_PORT -e GRAPHITE_HOST=$GRAPHITE_HOST -e GRAPHITE_PORT=$GRAPHITE_PORT starbuxman/product-service
4 |
--------------------------------------------------------------------------------
/server.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/joshlong-attic/the-operationalized-application/6a22b8501ef0a00402007ebb0b98dc8155efdf9b/server.jks
--------------------------------------------------------------------------------
/sonarqube.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | # see https://github.com/SonarSource/docker-sonarqube for more
6 |
7 | docker run -d --name sonarqube -p 9000:9000 -p 9092:9092 sonarqube:5.1
8 | mvn sonar:sonar -Dsonar.host.url=http://$(boot2docker ip):9000 -Dsonar.jdbc.url="jdbc:h2:tcp://$(boot2docker ip)/sonar"
9 |
--------------------------------------------------------------------------------
/src/main/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM java:8
2 | VOLUME /tmp
3 | EXPOSE 9000 8000 8081 8080
4 | ADD product-service.jar app.jar
5 | ADD classes/server.jks server.jks
6 | RUN bash -c 'touch /app.jar'
7 | ENTRYPOINT ["java", "-Dserver.ssl.key-store=/server.jks","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
8 |
--------------------------------------------------------------------------------
/src/main/java/demo/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import com.codahale.metrics.MetricRegistry;
4 | import com.codahale.metrics.graphite.Graphite;
5 | import com.codahale.metrics.graphite.GraphiteReporter;
6 | import net.logstash.logback.marker.LogstashMarker;
7 | import net.logstash.logback.marker.Markers;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.springframework.beans.factory.annotation.Autowired;
11 | import org.springframework.beans.factory.annotation.Value;
12 | import org.springframework.boot.CommandLineRunner;
13 | import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter;
14 | import org.springframework.boot.actuate.health.Health;
15 | import org.springframework.boot.actuate.health.HealthIndicator;
16 | import org.springframework.boot.actuate.metrics.CounterService;
17 | import org.springframework.boot.actuate.metrics.statsd.StatsdMetricWriter;
18 | import org.springframework.boot.actuate.system.ApplicationPidFileWriter;
19 | import org.springframework.boot.autoconfigure.SpringBootApplication;
20 | import org.springframework.boot.builder.SpringApplicationBuilder;
21 | import org.springframework.context.annotation.Bean;
22 | import org.springframework.data.rest.core.annotation.HandleAfterCreate;
23 | import org.springframework.data.rest.core.annotation.HandleAfterDelete;
24 | import org.springframework.data.rest.core.annotation.HandleAfterSave;
25 | import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
26 | import org.springframework.stereotype.Component;
27 |
28 | import java.util.concurrent.TimeUnit;
29 | import java.util.stream.IntStream;
30 |
31 | @SpringBootApplication
32 | public class DemoApplication {
33 |
34 |
35 | private static Logger LOGGER = LoggerFactory.getLogger(
36 | DemoApplication.class);
37 |
38 |
39 | @Bean
40 | HealthIndicator healthIndicator() {
41 | return () -> Health.status("I <3 Spring!").build();
42 | }
43 |
44 | @Bean
45 | GraphiteReporter graphiteReporter(MetricRegistry registry,
46 | @Value("${graphite.host}") String host,
47 | @Value("${graphite.port}") int port) {
48 |
49 | GraphiteReporter reporter = GraphiteReporter
50 | .forRegistry(registry)
51 | .prefixedWith("products")
52 | .build(new Graphite(host, port));
53 |
54 | reporter.start(2, TimeUnit.SECONDS);
55 |
56 | return reporter;
57 | }
58 | /*
59 | @Bean
60 | @ExportMetricWriter
61 | StatsdMetricWriter statsdMetricWriter(
62 | @Value("${statsd.host}") String host,
63 | @Value("${statsd.port}") int port) {
64 | return new StatsdMetricWriter("statsd-products", host, port);
65 | }*/
66 |
67 |
68 | @Bean
69 | CommandLineRunner dummy(ProductRepository pr) {
70 | return args ->
71 | IntStream.range(0, 1000)
72 | .forEach(i -> pr.save(new Product("sku" + i)));
73 | }
74 |
75 | @Component
76 | @RepositoryEventHandler
77 | public static class ProductEventHandler {
78 |
79 | @Autowired
80 | private CounterService counterService;
81 |
82 |
83 | @HandleAfterCreate
84 | public void create(Product p) {
85 | count("products.create", p);
86 | }
87 |
88 | @HandleAfterSave
89 | public void save(Product p) {
90 | count("products.save", p);
91 | count("products." + p.getId() + ".save", p);
92 | }
93 |
94 | @HandleAfterDelete
95 | public void delete(Product p) {
96 | count("products.delete", p);
97 | }
98 |
99 | private void count(String evt, Product p) {
100 | LogstashMarker logstashMarker = Markers.append("event", evt)
101 | .and(Markers.append("sku", p.getSku()))
102 | .and(Markers.append("id", p.getId()));
103 |
104 | LOGGER.info(logstashMarker, evt);
105 |
106 | this.counterService.increment(evt);
107 | this.counterService.increment("meter." + evt);
108 | }
109 | }
110 |
111 | public static void main(String[] args) {
112 | //SpringApplication.run(DemoApplication.class, args);
113 | new SpringApplicationBuilder(DemoApplication.class)
114 | .listeners(new ApplicationPidFileWriter())
115 | .run(args);
116 | }
117 | }
118 |
119 |
--------------------------------------------------------------------------------
/src/main/java/demo/Product.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import javax.persistence.Entity;
4 | import javax.persistence.GeneratedValue;
5 | import javax.persistence.Id;
6 |
7 |
8 | @Entity
9 | public class Product {
10 | @Id
11 | @GeneratedValue
12 | private Long id;
13 |
14 | private String sku;
15 |
16 | @Override
17 | public String toString() {
18 | return "Product{" + "id=" + id +
19 | ", sku='" + sku + '\'' +
20 | '}';
21 | }
22 |
23 | Product() { // why JPA why??
24 | }
25 |
26 | public Product(String sku) {
27 | this.sku = sku;
28 | }
29 |
30 | public Long getId() {
31 | return id;
32 | }
33 |
34 | public String getSku() {
35 | return sku;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/demo/ProductRepository.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import org.springframework.data.jpa.repository.JpaRepository;
4 | import org.springframework.data.repository.query.Param;
5 | import org.springframework.data.rest.core.annotation.RepositoryRestResource;
6 | import org.springframework.data.rest.core.annotation.RestResource;
7 |
8 | import java.util.Collection;
9 |
10 | @RepositoryRestResource
11 | public interface ProductRepository extends JpaRepository {
12 |
13 | @RestResource (path = "by-sku")
14 | Collection findBySku (@Param("sku") String sku) ;
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | management.context-path=/admin
2 |
3 | info.build.artifact=${project.artifactId}
4 | info.build.version=${project.version}
5 |
6 |
7 | server.ssl.key-store = server.jks
8 | server.ssl.key-store-password = password
9 | server.ssl.key-password = password
10 |
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 192.168.99.100
7 | 10042
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/test/java/demo/DemoApplicationTests.java:
--------------------------------------------------------------------------------
1 | package demo;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.test.context.web.WebAppConfiguration;
6 | import org.springframework.boot.test.SpringApplicationConfiguration;
7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
8 |
9 | @RunWith(SpringJUnit4ClassRunner.class)
10 | @SpringApplicationConfiguration(classes = DemoApplication.class)
11 | @WebAppConfiguration
12 | public class DemoApplicationTests {
13 |
14 | @Test
15 | public void contextLoads() {
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------