Ajay Yadav commited on
Commit
442299c
·
1 Parent(s): 9dc2ed2

Initial deployment of da-discovery-dev

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Dockerfile +27 -0
  2. README.md +34 -6
  3. build.gradle.kts +329 -0
  4. src/main/docker/Dockerfile +23 -0
  5. src/main/docker/Dockerfile.alpine-jlink +43 -0
  6. src/main/docker/Dockerfile.layered +34 -0
  7. src/main/docker/Dockerfile.native +20 -0
  8. src/main/docker/app.yml +131 -0
  9. src/main/docker/central-server-config/README.md +8 -0
  10. src/main/docker/central-server-config/docker-config/application.yml +16 -0
  11. src/main/docker/central-server-config/localhost-config/application.yml +16 -0
  12. src/main/docker/config/mysql/my.cnf +82 -0
  13. src/main/docker/grafana/provisioning/dashboards/JVM.json +3778 -0
  14. src/main/docker/grafana/provisioning/dashboards/dashboard.yml +11 -0
  15. src/main/docker/grafana/provisioning/datasources/datasource.yml +50 -0
  16. src/main/docker/init-scripts/00-create-multiple-databases.sh +24 -0
  17. src/main/docker/init-scripts/01-init-extensions.sql +42 -0
  18. src/main/docker/init-scripts/02-create-database-users.sql +32 -0
  19. src/main/docker/init-scripts/README.md +40 -0
  20. src/main/docker/init-scripts/run-postgresql.cmd +52 -0
  21. src/main/docker/init-scripts/run-postgresql.sh +57 -0
  22. src/main/docker/jhipster-control-center.yml +52 -0
  23. src/main/docker/jhipster-registry.yml +34 -0
  24. src/main/docker/jib/entrypoint.sh +39 -0
  25. src/main/docker/kafka.yml +60 -0
  26. src/main/docker/keycloak.yml +28 -0
  27. src/main/docker/monitoring.yml +31 -0
  28. src/main/docker/postgresql.yml +56 -0
  29. src/main/docker/prometheus/prometheus.yml +31 -0
  30. src/main/docker/realm-config/jhipster-realm.json +2351 -0
  31. src/main/docker/realm-config/keycloak-health-check.sh +12 -0
  32. src/main/docker/services.yml +52 -0
  33. src/main/docker/shell-wrapper.sh +12 -0
  34. src/main/docker/sonar.yml +15 -0
  35. src/main/docker/zipkin.yml +7 -0
  36. src/main/java/com/dalab/discovery/application/DADiscoveryAgent.java +238 -0
  37. src/main/java/com/dalab/discovery/catalog/client/CatalogClient.java +218 -0
  38. src/main/java/com/dalab/discovery/catalog/client/impl/DefaultCatalogClient.java +563 -0
  39. src/main/java/com/dalab/discovery/catalog/config/CatalogClientConfig.java +78 -0
  40. src/main/java/com/dalab/discovery/catalog/config/PersistenceConfig.java +134 -0
  41. src/main/java/com/dalab/discovery/catalog/config/ProdStagingPersistenceConfig.java +26 -0
  42. src/main/java/com/dalab/discovery/catalog/persistence/CloudHierarchyRegistry.java +163 -0
  43. src/main/java/com/dalab/discovery/catalog/persistence/IResourceCrawlerRegistry.java +81 -0
  44. src/main/java/com/dalab/discovery/catalog/persistence/impl/DefaultResourceCrawlerRegistry.java +185 -0
  45. src/main/java/com/dalab/discovery/catalog/service/CatalogLiterals.java +43 -0
  46. src/main/java/com/dalab/discovery/catalog/service/ICatalogService.java +148 -0
  47. src/main/java/com/dalab/discovery/catalog/service/UnityCatalogManager.java +102 -0
  48. src/main/java/com/dalab/discovery/catalog/service/impl/CatalogServiceImpl.java +501 -0
  49. src/main/java/com/dalab/discovery/client/cli/ApplicationCommands.java +71 -0
  50. src/main/java/com/dalab/discovery/client/cli/DGCommandLine.java +68 -0
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM openjdk:21-jdk-slim
2
+
3
+ WORKDIR /app
4
+
5
+ # Install required packages
6
+ RUN apt-get update && apt-get install -y \
7
+ curl \
8
+ wget \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Copy application files
12
+ COPY . .
13
+
14
+ # Build application (if build.gradle.kts exists)
15
+ RUN if [ -f "build.gradle.kts" ]; then \
16
+ ./gradlew build -x test; \
17
+ fi
18
+
19
+ # Expose port
20
+ EXPOSE 8080
21
+
22
+ # Health check
23
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
24
+ CMD curl -f http://localhost:8080/actuator/health || exit 1
25
+
26
+ # Run application
27
+ CMD ["java", "-jar", "build/libs/da-discovery.jar"]
README.md CHANGED
@@ -1,10 +1,38 @@
1
  ---
2
- title: Da Discovery Dev
3
- emoji: 📚
4
- colorFrom: gray
5
- colorTo: blue
6
  sdk: docker
7
- pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: da-discovery (dev)
3
+ emoji: 🔧
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: docker
7
+ app_port: 8080
8
  ---
9
 
10
+ # da-discovery - dev Environment
11
+
12
+ This is the da-discovery microservice deployed in the dev environment.
13
+
14
+ ## Features
15
+
16
+ - RESTful API endpoints
17
+ - Health monitoring via Actuator
18
+ - JWT authentication integration
19
+ - PostgreSQL database connectivity
20
+
21
+ ## API Documentation
22
+
23
+ Once deployed, API documentation will be available at:
24
+ - Swagger UI: https://huggingface.co/spaces/dalabsai/da-discovery-dev/swagger-ui.html
25
+ - Health Check: https://huggingface.co/spaces/dalabsai/da-discovery-dev/actuator/health
26
+
27
+ ## Environment
28
+
29
+ - **Environment**: dev
30
+ - **Port**: 8080
31
+ - **Java Version**: 21
32
+ - **Framework**: Spring Boot
33
+
34
+ ## Deployment
35
+
36
+ This service is automatically deployed via the DALab CI/CD pipeline.
37
+
38
+ Last updated: 2025-06-16 23:39:34
build.gradle.kts ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ plugins {
2
+ java
3
+ id("org.springframework.boot") version "3.2.5"
4
+ id("io.spring.dependency-management") version "1.1.4"
5
+ id("org.liquibase.gradle") version "2.2.0" // Check for latest Liquibase plugin version
6
+ id("checkstyle")
7
+ jacoco
8
+ id("com.google.cloud.tools.jib") version "3.4.0" // Jib plugin for Docker image building
9
+ // id("com.diffplug.spotless") version "6.25.0" // Spotless plugin
10
+ // Consider adding other plugins like git-commit-id if needed
11
+ }
12
+
13
+ group = "com.dalab.discovery"
14
+ version = "0.0.1-SNAPSHOT"
15
+
16
+ java {
17
+ sourceCompatibility = JavaVersion.VERSION_21
18
+ targetCompatibility = JavaVersion.VERSION_21
19
+ }
20
+
21
+ // Define versions from pom.xml properties
22
+ val jhipsterDependenciesVersion = "8.1.0"
23
+ val hibernateVersion = "6.4.0.Final"
24
+ val archunitJunit5Version = "1.2.1"
25
+ val springCloudGcpVersion = "6.1.1" // Note: Using spring-cloud-gcp.version from pom
26
+ val googleCloudLibrariesBomVersion = "26.60.0"
27
+ val googleAuthVersion = "1.23.0"
28
+ val googleCloudAssetVersion = "3.41.0"
29
+ val awsSdkVersion = "2.25.1" // Assuming AWS SDK v2
30
+ val azureIdentityVersion = "1.11.0"
31
+ val azureCoreVersion = "1.45.0"
32
+ val azureResourcemanagerVersion = "2.31.0"
33
+ val azureStorageBlobVersion = "12.25.0"
34
+ val azureCosmosVersion = "4.54.0"
35
+ val azureMonitorQueryVersion = "1.5.7"
36
+ val ociSdkVersion = "3.22.0"
37
+ val mapstructVersion = "1.5.5.Final" // Added MapStruct version
38
+
39
+ repositories {
40
+ mavenCentral()
41
+ maven { url = uri("https://maven.google.com") }
42
+ }
43
+
44
+ // Add resolution strategy for dependency conflicts
45
+ configurations.all {
46
+ resolutionStrategy {
47
+ // Force Guava version and exclude the outdated google-collections
48
+ force("com.google.guava:guava:33.4.0-jre")
49
+ exclude(group = "com.google.collections", module = "google-collections")
50
+ }
51
+ }
52
+
53
+ dependencyManagement {
54
+ imports {
55
+ mavenBom("tech.jhipster:jhipster-dependencies:${jhipsterDependenciesVersion}")
56
+ mavenBom("com.google.cloud:spring-cloud-gcp-dependencies:${springCloudGcpVersion}")
57
+ mavenBom("com.google.cloud:libraries-bom:${googleCloudLibrariesBomVersion}")
58
+ mavenBom("org.springframework.shell:spring-shell-dependencies:3.2.0")
59
+ // Potentially add AWS SDK BOM if needed: software.amazon.awssdk:bom:${awsSdkVersion}
60
+ // Potentially add Azure SDK BOM if needed: com.azure:azure-sdk-bom:...
61
+ }
62
+ }
63
+
64
+ dependencies {
65
+
66
+ implementation("tech.jhipster:jhipster-framework")
67
+ implementation("org.springframework.boot:spring-boot-starter-actuator")
68
+ implementation("org.springframework.boot:spring-boot-starter-cache")
69
+ implementation("org.springframework.boot:spring-boot-starter-data-jpa")
70
+ implementation("org.springframework.boot:spring-boot-starter-logging")
71
+ implementation("org.springframework.boot:spring-boot-starter-mail")
72
+ implementation("org.springframework.boot:spring-boot-starter-oauth2-client")
73
+ implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
74
+ implementation("org.springframework.boot:spring-boot-starter-thymeleaf")
75
+ implementation("org.springframework.boot:spring-boot-starter-undertow")
76
+ implementation("org.springframework.boot:spring-boot-starter-web")
77
+ implementation("org.springframework.boot:spring-boot-starter-mail")
78
+ implementation("org.springframework.security:spring-security-data")
79
+ implementation("org.springdoc:springdoc-openapi-starter-webmvc-api")
80
+ implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate6")
81
+ implementation("com.fasterxml.jackson.datatype:jackson-datatype-hppc")
82
+ implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
83
+ implementation("com.fasterxml.jackson.module:jackson-module-jaxb-annotations")
84
+ implementation("com.github.ben-manes.caffeine:caffeine")
85
+ implementation("com.mysql:mysql-connector-j")
86
+ implementation("com.zaxxer:HikariCP")
87
+ implementation("io.dropwizard.metrics:metrics-core")
88
+ implementation("io.micrometer:micrometer-registry-prometheus")
89
+ implementation("jakarta.annotation:jakarta.annotation-api")
90
+ implementation("javax.cache:cache-api")
91
+ implementation("org.apache.commons:commons-lang3")
92
+ implementation("org.ehcache:ehcache")
93
+ implementation("org.hibernate.orm:hibernate-core:${hibernateVersion}")
94
+ implementation("org.hibernate.orm:hibernate-jcache:${hibernateVersion}")
95
+ implementation("org.hibernate.validator:hibernate-validator")
96
+ implementation("org.liquibase:liquibase-core") // Version managed by BOM or plugin
97
+ implementation("org.springframework.cloud:spring-cloud-starter")
98
+ implementation("org.springframework.cloud:spring-cloud-starter-bootstrap")
99
+ implementation("org.springframework.cloud:spring-cloud-starter-circuitbreaker-resilience4j")
100
+ implementation("org.springframework.cloud:spring-cloud-starter-config")
101
+ implementation("org.springframework.kafka:spring-kafka")
102
+ implementation("org.springframework.retry:spring-retry:2.0.5")
103
+ implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client")
104
+ implementation("org.springframework.data:spring-data-r2dbc:3.3.2")
105
+ implementation("org.springframework.cloud:spring-cloud-starter-stream-kafka")
106
+ implementation("org.assertj:assertj-core:3.13.2")
107
+ implementation("org.fusesource.jansi:jansi:2.4.0")
108
+
109
+ // Google Cloud Dependencies (Versions from BOMs)
110
+ // implementation("com.google.cloud:google-cloud-sql-connector-jdbc") // Replaced with starter below
111
+ implementation("com.google.cloud:spring-cloud-gcp-starter-sql-postgresql")
112
+ implementation("com.google.cloud:google-cloud-compute")
113
+ implementation("com.google.cloud:google-cloud-bigquery")
114
+ implementation("com.google.cloud:google-cloud-storage")
115
+ implementation("com.google.cloud:google-cloud-resourcemanager")
116
+ implementation("com.google.cloud:google-cloud-logging")
117
+ implementation("com.google.cloud:google-cloud-storage-control")
118
+
119
+ // Other Google Cloud Dependencies (Explicit Versions)
120
+ implementation("com.google.auth:google-auth-library-oauth2-http")
121
+ implementation("com.google.auth:google-auth-library-credentials")
122
+ //implementation("com.google.auth:google-auth-library-oauth2-http:${googleAuthVersion}")
123
+ implementation("com.google.cloud:google-cloud-asset:${googleCloudAssetVersion}")
124
+ //implementation("com.google.auth:google-auth-library-credentials:${googleAuthVersion}")
125
+ implementation("com.google.apis:google-api-services-cloudresourcemanager:v3-rev20240310-2.0.0")
126
+ implementation("com.google.apis:google-api-services-sqladmin:v1-rev20250310-2.0.0")
127
+
128
+ // AWS SDK Dependencies (Using specific versions for now)
129
+ // Consider using the AWS SDK BOM: implementation(platform("software.amazon.awssdk:bom:${awsSdkVersion}"))
130
+ compileOnly("software.amazon.awssdk:aws-sdk-java:${awsSdkVersion}") // compileOnly maps to <optional>true + <scope>compile
131
+ implementation("software.amazon.awssdk:ec2:${awsSdkVersion}")
132
+ implementation("software.amazon.awssdk:s3:${awsSdkVersion}")
133
+ implementation("software.amazon.awssdk:rds:${awsSdkVersion}")
134
+
135
+ // Azure SDK Dependencies (Using specific versions for now)
136
+ // Consider using the Azure SDK BOM: implementation(platform("com.azure:azure-sdk-bom:..."))
137
+ implementation("com.azure:azure-identity:${azureIdentityVersion}")
138
+ implementation("com.azure:azure-core:${azureCoreVersion}")
139
+ implementation("com.azure.resourcemanager:azure-resourcemanager:${azureResourcemanagerVersion}")
140
+ implementation("com.azure:azure-storage-blob:${azureStorageBlobVersion}")
141
+ implementation("com.azure:azure-cosmos:${azureCosmosVersion}")
142
+ implementation("com.azure:azure-monitor-query:${azureMonitorQueryVersion}")
143
+
144
+ // Oracle Cloud SDK Dependencies
145
+ implementation("com.oracle.oci.sdk:oci-java-sdk-common:${ociSdkVersion}")
146
+ implementation("com.oracle.oci.sdk:oci-java-sdk-core:${ociSdkVersion}")
147
+ implementation("com.oracle.oci.sdk:oci-java-sdk-objectstorage:${ociSdkVersion}")
148
+ implementation("com.oracle.oci.sdk:oci-java-sdk-database:${ociSdkVersion}")
149
+ implementation("com.oracle.oci.sdk:oci-java-sdk-identity:${ociSdkVersion}")
150
+ implementation("com.oracle.oci.sdk:oci-java-sdk-audit:${ociSdkVersion}")
151
+
152
+ // Annotation Processors & Provided Dependencies
153
+ annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
154
+ compileOnly("org.hibernate.orm:hibernate-jpamodelgen")
155
+ annotationProcessor("org.hibernate.orm:hibernate-jpamodelgen:${hibernateVersion}")
156
+
157
+ // Test Dependencies
158
+ testImplementation("org.springframework.boot:spring-boot-starter-test")
159
+ testImplementation("org.springframework.boot:spring-boot-test")
160
+ testImplementation("org.springframework.security:spring-security-test")
161
+ testImplementation("com.tngtech.archunit:archunit-junit5-api:${archunitJunit5Version}")
162
+ testRuntimeOnly("com.tngtech.archunit:archunit-junit5-engine:${archunitJunit5Version}")
163
+ testImplementation("org.testcontainers:jdbc")
164
+ testImplementation("org.testcontainers:mysql")
165
+ testImplementation("com.h2database:h2:2.2.224")
166
+
167
+ // Spring Data JPA
168
+ implementation("org.springframework.boot:spring-boot-starter-data-jpa")
169
+
170
+ // Database drivers - PostgreSQL as primary
171
+ implementation("org.postgresql:postgresql:42.6.0")
172
+ // implementation("com.h2database:h2:2.2.224") // Keep H2 for testing - REMOVED due to being in 'implementation'
173
+
174
+ // Hibernate Types for JSON support - REMOVING THIS as Hibernate 6.4+ has better native support
175
+ // implementation("com.vladmihalcea:hibernate-types-60:2.21.1") {
176
+ // transitive = true
177
+ // }
178
+
179
+ // Jackson types
180
+ implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate6")
181
+
182
+ // Lombok for reducing boilerplate
183
+ compileOnly("org.projectlombok:lombok")
184
+ annotationProcessor("org.projectlombok:lombok")
185
+
186
+ implementation("org.springframework.boot:spring-boot-starter-aop") // Required for @Retryable
187
+
188
+ implementation("org.springframework.shell:spring-shell-starter")
189
+
190
+ // MapStruct
191
+ implementation("org.mapstruct:mapstruct:${mapstructVersion}")
192
+ annotationProcessor("org.mapstruct:mapstruct-processor:${mapstructVersion}")
193
+ annotationProcessor("org.projectlombok:lombok-mapstruct-binding:0.2.0") // For Lombok and MapStruct integration
194
+
195
+ // OpenFeign for declarative REST clients
196
+ implementation("org.springframework.cloud:spring-cloud-starter-openfeign")
197
+ }
198
+
199
+ // Spring Boot configuration
200
+ springBoot {
201
+ mainClass.set("com.dalab.discovery.application.DADiscoveryAgent")
202
+ }
203
+
204
+ tasks.withType<Test> {
205
+ useJUnitPlatform()
206
+ systemProperty("spring.profiles.active", "test")
207
+ // Enable ByteBuddy experimental mode for Java 24 compatibility
208
+ systemProperty("net.bytebuddy.experimental", "true")
209
+
210
+ // Enable parallel test execution
211
+ maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).takeIf { it > 0 } ?: 1
212
+
213
+
214
+ // Exclude tests that require specific infrastructure or Docker
215
+ // exclude {
216
+ // // it.name.contains("TechnicalStructureTest") ||
217
+ // // it.name.contains("UserResourceIT") ||
218
+ // // it.name.contains("PublicUserResourceIT") ||
219
+ // // it.name.contains("AWSConfigServiceTest") ||
220
+ // // it.name.contains("HibernateTimeZoneIT") ||
221
+ // // it.name.contains("CrawlerIntegrationTest")
222
+ // }
223
+
224
+ testLogging {
225
+ events("passed", "skipped", "failed")
226
+ showExceptions = true
227
+ showCauses = true
228
+ showStackTraces = true
229
+ exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
230
+
231
+ // Enable test output on console
232
+ showStandardStreams = true
233
+ }
234
+ }
235
+
236
+ // Checkstyle Configuration
237
+ checkstyle {
238
+ toolVersion = "10.12.5" // Use version from pom
239
+ configFile = file("checkstyle.xml") // Assumes checkstyle.xml is in root
240
+ // You might need to configure source sets if they differ from default
241
+ }
242
+
243
+ // Configure configurations to exclude google-collections
244
+ configurations.checkstyle {
245
+ exclude(group = "com.google.collections", module = "google-collections")
246
+ }
247
+
248
+ // Disable checkstyle tasks to avoid build failures
249
+ tasks.withType<Checkstyle> {
250
+ enabled = false
251
+ }
252
+
253
+ tasks.checkstyleMain {
254
+ source("src/main/java")
255
+ }
256
+
257
+ tasks.checkstyleTest {
258
+ source("src/test/java")
259
+ }
260
+
261
+ // JaCoCo Configuration
262
+ jacoco {
263
+ toolVersion = "0.8.11" // Use version from pom
264
+ }
265
+
266
+ tasks.jacocoTestReport {
267
+ reports {
268
+ xml.required.set(true)
269
+ csv.required.set(false)
270
+ html.outputLocation.set(layout.buildDirectory.dir("jacocoHtml"))
271
+ }
272
+ // Depends on test task completion
273
+ dependsOn(tasks.test)
274
+ }
275
+
276
+ // Liquibase Configuration (Basic setup, might need more config based on pom)
277
+ // liquibase {
278
+ // activities {
279
+ // main {
280
+ // changeLogFile("config/liquibase/master.xml")
281
+ // url = "jdbc:mysql://localhost:3306/DADiscovery" // Example, configure appropriately
282
+ // username = "root"
283
+ // password = ""
284
+ // // contexts = "!test" // How contexts are handled differs
285
+ // }
286
+ // }
287
+ // }
288
+
289
+ // Configure Java Compile to use annotationProcessor configuration
290
+ tasks.withType<JavaCompile> {
291
+ options.encoding = "UTF-8"
292
+ options.annotationProcessorPath = configurations.annotationProcessor.get()
293
+ // Increase memory for Java compilation
294
+ options.forkOptions.jvmArgs = listOf("-Xmx1g")
295
+ }
296
+
297
+ // Task to help VS Code recognize dependencies
298
+ tasks.register("createClasspath") {
299
+ doLast {
300
+ val classpath = configurations.runtimeClasspath.get()
301
+ .resolvedConfiguration.resolvedArtifacts
302
+ .joinToString("\n") { it.file.absolutePath }
303
+
304
+ file(".classpath").writeText(classpath)
305
+
306
+ println("Dependencies written to .classpath file")
307
+ println("Spring Shell JAR locations:")
308
+ configurations.runtimeClasspath.get().files
309
+ .filter { it.name.contains("spring-shell") }
310
+ .forEach { println(it.absolutePath) }
311
+ }
312
+ }
313
+
314
+ // Add Jib configuration for Docker image building
315
+ jib {
316
+ from {
317
+ image = "openjdk:21-slim"
318
+ }
319
+ to {
320
+ image = "da-discovery"
321
+ tags = setOf("latest")
322
+ }
323
+ container {
324
+ creationTime = "USE_CURRENT_TIMESTAMP"
325
+ jvmFlags = listOf("-Xmx512m", "-Xms256m")
326
+ ports = listOf("8080")
327
+ workingDirectory = "/app"
328
+ }
329
+ }
src/main/docker/Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultra-lean container using Google Distroless
2
+ # Expected final size: ~120-180MB (minimal base + JRE + JAR only)
3
+
4
+ FROM gcr.io/distroless/java21-debian12:nonroot
5
+
6
+ # Set working directory
7
+ WORKDIR /app
8
+
9
+ # Copy JAR file
10
+ COPY build/libs/da-discovery.jar app.jar
11
+
12
+ # Expose standard Spring Boot port
13
+ EXPOSE 8080
14
+
15
+ # Run application (distroless has no shell, so use exec form)
16
+ ENTRYPOINT ["java", \
17
+ "-XX:+UseContainerSupport", \
18
+ "-XX:MaxRAMPercentage=75.0", \
19
+ "-XX:+UseG1GC", \
20
+ "-XX:+UseStringDeduplication", \
21
+ "-Djava.security.egd=file:/dev/./urandom", \
22
+ "-Dspring.backgroundpreinitializer.ignore=true", \
23
+ "-jar", "app.jar"]
src/main/docker/Dockerfile.alpine-jlink ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultra-minimal Alpine + Custom JRE
2
+ # Expected size: ~120-160MB
3
+
4
+ # Stage 1: Create custom JRE with only needed modules
5
+ FROM eclipse-temurin:21-jdk-alpine as jre-builder
6
+ WORKDIR /app
7
+
8
+ # Analyze JAR to find required modules
9
+ COPY build/libs/*.jar app.jar
10
+ RUN jdeps --ignore-missing-deps --print-module-deps app.jar > modules.txt
11
+
12
+ # Create minimal JRE with only required modules
13
+ RUN jlink \
14
+ --add-modules $(cat modules.txt),java.logging,java.xml,java.sql,java.naming,java.desktop,java.management,java.security.jgss,java.instrument \
15
+ --strip-debug \
16
+ --no-man-pages \
17
+ --no-header-files \
18
+ --compress=2 \
19
+ --output /custom-jre
20
+
21
+ # Stage 2: Production image
22
+ FROM alpine:3.19
23
+ RUN apk add --no-cache tzdata && \
24
+ addgroup -g 1001 -S appgroup && \
25
+ adduser -u 1001 -S appuser -G appgroup
26
+
27
+ # Copy custom JRE
28
+ COPY --from=jre-builder /custom-jre /opt/java
29
+ ENV JAVA_HOME=/opt/java
30
+ ENV PATH="$JAVA_HOME/bin:$PATH"
31
+
32
+ WORKDIR /app
33
+ COPY build/libs/*.jar app.jar
34
+ RUN chown appuser:appgroup app.jar
35
+
36
+ USER appuser
37
+ EXPOSE 8080
38
+
39
+ ENTRYPOINT ["java", \
40
+ "-XX:+UseContainerSupport", \
41
+ "-XX:MaxRAMPercentage=70.0", \
42
+ "-XX:+UseG1GC", \
43
+ "-jar", "app.jar"]
src/main/docker/Dockerfile.layered ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Ultra-optimized layered build using Distroless
2
+ # Expected size: ~180-220MB with better caching
3
+
4
+ FROM gcr.io/distroless/java21-debian12:nonroot as base
5
+
6
+ # Stage 1: Extract JAR layers for optimal caching
7
+ FROM eclipse-temurin:21-jdk-alpine as extractor
8
+ WORKDIR /app
9
+ COPY build/libs/*.jar app.jar
10
+ RUN java -Djarmode=layertools -jar app.jar extract
11
+
12
+ # Stage 2: Production image with extracted layers
13
+ FROM base
14
+ WORKDIR /app
15
+
16
+ # Copy layers in dependency order (best caching)
17
+ COPY --from=extractor /app/dependencies/ ./
18
+ COPY --from=extractor /app/spring-boot-loader/ ./
19
+ COPY --from=extractor /app/snapshot-dependencies/ ./
20
+ COPY --from=extractor /app/application/ ./
21
+
22
+ EXPOSE 8080
23
+
24
+ # Optimized JVM settings for micro-containers
25
+ ENTRYPOINT ["java", \
26
+ "-XX:+UseContainerSupport", \
27
+ "-XX:MaxRAMPercentage=70.0", \
28
+ "-XX:+UseG1GC", \
29
+ "-XX:+UseStringDeduplication", \
30
+ "-XX:+CompactStrings", \
31
+ "-Xshare:on", \
32
+ "-Djava.security.egd=file:/dev/./urandom", \
33
+ "-Dspring.backgroundpreinitializer.ignore=true", \
34
+ "org.springframework.boot.loader.JarLauncher"]
src/main/docker/Dockerfile.native ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GraalVM Native Image - Ultra-fast startup, tiny size
2
+ # Expected size: ~50-80MB, startup <100ms
3
+ # Note: Requires native compilation support in Spring Boot
4
+
5
+ # Stage 1: Native compilation
6
+ FROM ghcr.io/graalvm/graalvm-ce:ol9-java21 as native-builder
7
+ WORKDIR /app
8
+
9
+ # Install native-image
10
+ RUN gu install native-image
11
+
12
+ # Copy source and build native executable
13
+ COPY . .
14
+ RUN ./gradlew nativeCompile
15
+
16
+ # Stage 2: Minimal runtime
17
+ FROM scratch
18
+ COPY --from=native-builder /app/build/native/nativeCompile/app /app
19
+ EXPOSE 8080
20
+ ENTRYPOINT ["/app"]
src/main/docker/app.yml ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production
2
+ name: da-discovery
3
+ services:
4
+ app:
5
+ image: da-discovery
6
+ environment:
7
+ - _JAVA_OPTIONS=-Xmx512m -Xms256m
8
+ - SPRING_PROFILES_ACTIVE=prod,api-docs
9
+ - MANAGEMENT_PROMETHEUS_METRICS_EXPORT_ENABLED=true
10
+ - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/eureka
11
+ - SPRING_CLOUD_CONFIG_URI=http://admin:$${jhipster.registry.password}@jhipster-registry:8761/config
12
+ - SPRING_DATASOURCE_URL=jdbc:postgresql://postgresql:5432/da-discovery
13
+ - SPRING_DATASOURCE_USERNAME=da-discovery
14
+ - SPRING_DATASOURCE_PASSWORD=da-discovery
15
+ - SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT=org.hibernate.dialect.PostgreSQLDialect
16
+ - SPRING_JPA_HIBERNATE_DDL_AUTO=none
17
+ - SPRING_LIQUIBASE_URL=jdbc:postgresql://postgresql:5432/da-discovery
18
+ - SPRING_LIQUIBASE_USER=da-discovery
19
+ - SPRING_LIQUIBASE_PASSWORD=da-discovery
20
+ - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=http://keycloak:8080/realms/dalab
21
+ - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID=internal
22
+ - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET=internal
23
+ - DISCOVERY_JOB_EXECUTOR_MODE=SPARK
24
+ healthcheck:
25
+ test:
26
+ - CMD
27
+ - curl
28
+ - -f
29
+ - http://localhost:8081/management/health
30
+ interval: 5s
31
+ timeout: 5s
32
+ retries: 40
33
+ depends_on:
34
+ postgresql:
35
+ condition: service_healthy
36
+ keycloak:
37
+ condition: service_healthy
38
+ jhipster-registry:
39
+ condition: service_healthy
40
+ ports:
41
+ - '8080:8080'
42
+ networks:
43
+ - da-discovery-network
44
+ postgresql:
45
+ image: postgres:15.1
46
+ volumes:
47
+ - postgresql-data:/var/lib/postgresql/data
48
+ - ./init-scripts:/docker-entrypoint-initdb.d
49
+ environment:
50
+ - POSTGRES_USER=da-discovery
51
+ - POSTGRES_PASSWORD=da-discovery
52
+ - POSTGRES_DB=da-discovery
53
+ - POSTGRES_MULTIPLE_DATABASES=da-discovery-test
54
+ # PostgreSQL optimization settings
55
+ - POSTGRES_INITDB_ARGS=--data-checksums
56
+ # Performance tuning
57
+ - POSTGRES_MAX_CONNECTIONS=100
58
+ - POSTGRES_SHARED_BUFFERS=256MB
59
+ - POSTGRES_EFFECTIVE_CACHE_SIZE=768MB
60
+ - POSTGRES_MAINTENANCE_WORK_MEM=64MB
61
+ - POSTGRES_WORK_MEM=4MB
62
+ ports:
63
+ - '5432:5432'
64
+ healthcheck:
65
+ test: ['CMD-SHELL', 'pg_isready -U da-discovery -d da-discovery']
66
+ interval: 5s
67
+ timeout: 5s
68
+ retries: 5
69
+ command:
70
+ - 'postgres'
71
+ - '-c'
72
+ - 'max_connections=100'
73
+ - '-c'
74
+ - 'shared_buffers=256MB'
75
+ - '-c'
76
+ - 'effective_cache_size=768MB'
77
+ - '-c'
78
+ - 'maintenance_work_mem=64MB'
79
+ - '-c'
80
+ - 'work_mem=4MB'
81
+ - '-c'
82
+ - 'log_min_duration_statement=1000'
83
+ - '-c'
84
+ - 'log_connections=on'
85
+ - '-c'
86
+ - 'log_disconnections=on'
87
+ restart: unless-stopped
88
+ networks:
89
+ - da-discovery-network
90
+ keycloak:
91
+ extends:
92
+ file: ./keycloak.yml
93
+ service: keycloak
94
+ networks:
95
+ - da-discovery-network
96
+ jhipster-registry:
97
+ extends:
98
+ file: ./jhipster-registry.yml
99
+ service: jhipster-registry
100
+ depends_on:
101
+ keycloak:
102
+ condition: service_healthy
103
+ networks:
104
+ - da-discovery-network
105
+ # Shell service for running commands (doesn't stay running)
106
+ shell:
107
+ image: da-discovery
108
+ entrypoint: ['java', '-jar', '/app/app.jar']
109
+ # Default command will run the shell in interactive mode
110
+ command: ['shell', '--spring.profiles.active=shell']
111
+ environment:
112
+ - _JAVA_OPTIONS=-Xmx512m -Xms256m
113
+ - SPRING_DATASOURCE_URL=jdbc:postgresql://postgresql:5432/da-discovery
114
+ - SPRING_DATASOURCE_USERNAME=da-discovery
115
+ - SPRING_DATASOURCE_PASSWORD=da-discovery
116
+ - KAFKA_BOOTSTRAP_SERVERS=kafka:29092
117
+ depends_on:
118
+ postgresql:
119
+ condition: service_healthy
120
+ profiles:
121
+ - shell
122
+ networks:
123
+ - da-discovery-network
124
+
125
+ volumes:
126
+ postgresql-data:
127
+ driver: local
128
+
129
+ networks:
130
+ da-discovery-network:
131
+ driver: bridge
src/main/docker/central-server-config/README.md ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Central configuration sources details
2
+
3
+ The JHipster-Registry will use the following directories as its configuration source :
4
+
5
+ - localhost-config : when running the registry in docker with the jhipster-registry.yml docker-compose file
6
+ - docker-config : when running the registry and the app both in docker with the app.yml docker-compose file
7
+
8
+ For more info, refer to https://www.jhipster.tech/jhipster-registry/#spring-cloud-config
src/main/docker/central-server-config/docker-config/application.yml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Common configuration shared between all applications
2
+ configserver:
3
+ name: Docker JHipster Registry
4
+ status: Connected to the JHipster Registry running in Docker
5
+
6
+ jhipster:
7
+ security:
8
+ authentication:
9
+ jwt:
10
+ # secret key which should be base64 encoded and changed in production
11
+ base64-secret: NDM0YjIyOWUxOTIzZTlhM2I0MDVmZWNlN2MwYTUyY2UzN2VhOTUxZTM4ZjNmNTg3ZDZiOTM1ZGEwNTJkN2UwZjc5MzFmYTMxNTZiNjk5NThkZTJjZWJlYmZkZmQ5OWVmOTFlZmYyODJlYmU0M2JlYTY4ZTRlOWRmNWFjMzliNzc=
12
+
13
+ eureka:
14
+ client:
15
+ service-url:
16
+ defaultZone: http://admin:${jhipster.registry.password}@jhipster-registry:8761/eureka/
src/main/docker/central-server-config/localhost-config/application.yml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Common configuration shared between all applications
2
+ configserver:
3
+ name: Docker JHipster Registry
4
+ status: Connected to the JHipster Registry running in Docker
5
+
6
+ jhipster:
7
+ security:
8
+ authentication:
9
+ jwt:
10
+ # secret key which should be base64 encoded and changed in production
11
+ base64-secret: NDM0YjIyOWUxOTIzZTlhM2I0MDVmZWNlN2MwYTUyY2UzN2VhOTUxZTM4ZjNmNTg3ZDZiOTM1ZGEwNTJkN2UwZjc5MzFmYTMxNTZiNjk5NThkZTJjZWJlYmZkZmQ5OWVmOTFlZmYyODJlYmU0M2JlYTY4ZTRlOWRmNWFjMzliNzc=
12
+
13
+ eureka:
14
+ client:
15
+ service-url:
16
+ defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
src/main/docker/config/mysql/my.cnf ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # For advice on how to change settings please see
2
+ # http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html
3
+ [mysqld]
4
+ user = mysql
5
+ datadir = /var/lib/mysql
6
+ port = 3306
7
+ #socket = /tmp/mysql.sock
8
+ skip-external-locking
9
+ key_buffer_size = 16K
10
+ max_allowed_packet = 1M
11
+ table_open_cache = 4
12
+ sort_buffer_size = 64K
13
+ read_buffer_size = 256K
14
+ read_rnd_buffer_size = 256K
15
+ net_buffer_length = 2K
16
+ skip-host-cache
17
+ skip-name-resolve
18
+
19
+ # Don't listen on a TCP/IP port at all. This can be a security enhancement,
20
+ # if all processes that need to connect to mysqld run on the same host.
21
+ # All interaction with mysqld must be made via Unix sockets or named pipes.
22
+ # Note that using this option without enabling named pipes on Windows
23
+ # (using the "enable-named-pipe" option) will render mysqld useless!
24
+ #
25
+ #skip-networking
26
+ #server-id = 1
27
+
28
+ # Uncomment the following if you want to log updates
29
+ #log-bin=mysql-bin
30
+
31
+ # binary logging format - mixed recommended
32
+ #binlog_format=mixed
33
+
34
+ # Causes updates to non-transactional engines using statement format to be
35
+ # written directly to binary log. Before using this option make sure that
36
+ # there are no dependencies between transactional and non-transactional
37
+ # tables such as in the statement INSERT INTO t_myisam SELECT * FROM
38
+ # t_innodb; otherwise, slaves may diverge from the master.
39
+ #binlog_direct_non_transactional_updates=TRUE
40
+
41
+ # Uncomment the following if you are using InnoDB tables
42
+ innodb_data_file_path = ibdata1:10M:autoextend
43
+ # You can set .._buffer_pool_size up to 50 - 80 %
44
+ # of RAM but beware of setting memory usage too high
45
+ innodb_buffer_pool_size = 16M
46
+ #innodb_additional_mem_pool_size = 2M
47
+ # Set .._log_file_size to 25 % of buffer pool size
48
+ innodb_log_file_size = 5M
49
+ innodb_log_buffer_size = 8M
50
+ innodb_flush_log_at_trx_commit = 1
51
+ innodb_lock_wait_timeout = 50
52
+
53
+ symbolic-links=0
54
+ innodb_buffer_pool_size=5M
55
+ innodb_log_buffer_size=256K
56
+ max_connections=20
57
+ key_buffer_size=8
58
+ thread_cache_size=0
59
+ host_cache_size=0
60
+ innodb_ft_cache_size=1600000
61
+ innodb_ft_total_cache_size=32000000
62
+ #### These optimize the memory use of MySQL
63
+ #### http://www.tocker.ca/2014/03/10/configuring-mysql-to-use-minimal-memory.html
64
+
65
+ # per thread or per operation settings
66
+ thread_stack=131072
67
+ sort_buffer_size=32K
68
+ read_buffer_size=8200
69
+ read_rnd_buffer_size=8200
70
+ max_heap_table_size=16K
71
+ tmp_table_size=1K
72
+ bulk_insert_buffer_size=0
73
+ join_buffer_size=128
74
+ net_buffer_length=1K
75
+ innodb_sort_buffer_size=64K
76
+
77
+ #settings that relate to the binary log (if enabled)
78
+ binlog_cache_size=4K
79
+ binlog_stmt_cache_size=4K
80
+
81
+ performance_schema = off
82
+ character-set-server = utf8mb4
src/main/docker/grafana/provisioning/dashboards/JVM.json ADDED
@@ -0,0 +1,3778 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "annotations": {
3
+ "list": [
4
+ {
5
+ "builtIn": 1,
6
+ "datasource": "-- Grafana --",
7
+ "enable": true,
8
+ "hide": true,
9
+ "iconColor": "rgba(0, 211, 255, 1)",
10
+ "limit": 100,
11
+ "name": "Annotations & Alerts",
12
+ "showIn": 0,
13
+ "type": "dashboard"
14
+ },
15
+ {
16
+ "datasource": "Prometheus",
17
+ "enable": true,
18
+ "expr": "resets(process_uptime_seconds{application=\"$application\", instance=\"$instance\"}[1m]) > 0",
19
+ "iconColor": "rgba(255, 96, 96, 1)",
20
+ "name": "Restart Detection",
21
+ "showIn": 0,
22
+ "step": "1m",
23
+ "tagKeys": "restart-tag",
24
+ "textFormat": "uptime reset",
25
+ "titleFormat": "Restart"
26
+ }
27
+ ]
28
+ },
29
+ "description": "Dashboard for Micrometer instrumented applications (Java, Spring Boot, Micronaut)",
30
+ "editable": true,
31
+ "gnetId": 4701,
32
+ "graphTooltip": 1,
33
+ "iteration": 1553765841423,
34
+ "links": [],
35
+ "panels": [
36
+ {
37
+ "content": "\n# Acknowledgments\n\nThank you to [Michael Weirauch](https://twitter.com/emwexx) for creating this dashboard: see original JVM (Micrometer) dashboard at [https://grafana.com/dashboards/4701](https://grafana.com/dashboards/4701)\n\n\n\n",
38
+ "gridPos": {
39
+ "h": 3,
40
+ "w": 24,
41
+ "x": 0,
42
+ "y": 0
43
+ },
44
+ "id": 141,
45
+ "links": [],
46
+ "mode": "markdown",
47
+ "timeFrom": null,
48
+ "timeShift": null,
49
+ "title": "Acknowledgments",
50
+ "type": "text"
51
+ },
52
+ {
53
+ "collapsed": false,
54
+ "gridPos": {
55
+ "h": 1,
56
+ "w": 24,
57
+ "x": 0,
58
+ "y": 3
59
+ },
60
+ "id": 125,
61
+ "panels": [],
62
+ "repeat": null,
63
+ "title": "Quick Facts",
64
+ "type": "row"
65
+ },
66
+ {
67
+ "cacheTimeout": null,
68
+ "colorBackground": false,
69
+ "colorValue": true,
70
+ "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
71
+ "datasource": "Prometheus",
72
+ "decimals": 1,
73
+ "editable": true,
74
+ "error": false,
75
+ "format": "s",
76
+ "gauge": {
77
+ "maxValue": 100,
78
+ "minValue": 0,
79
+ "show": false,
80
+ "thresholdLabels": false,
81
+ "thresholdMarkers": true
82
+ },
83
+ "gridPos": {
84
+ "h": 3,
85
+ "w": 6,
86
+ "x": 0,
87
+ "y": 4
88
+ },
89
+ "height": "",
90
+ "id": 63,
91
+ "interval": null,
92
+ "links": [],
93
+ "mappingType": 1,
94
+ "mappingTypes": [
95
+ {
96
+ "name": "value to text",
97
+ "value": 1
98
+ },
99
+ {
100
+ "name": "range to text",
101
+ "value": 2
102
+ }
103
+ ],
104
+ "maxDataPoints": 100,
105
+ "nullPointMode": "connected",
106
+ "nullText": null,
107
+ "postfix": "",
108
+ "postfixFontSize": "50%",
109
+ "prefix": "",
110
+ "prefixFontSize": "70%",
111
+ "rangeMaps": [
112
+ {
113
+ "from": "null",
114
+ "text": "N/A",
115
+ "to": "null"
116
+ }
117
+ ],
118
+ "sparkline": {
119
+ "fillColor": "rgba(31, 118, 189, 0.18)",
120
+ "full": false,
121
+ "lineColor": "rgb(31, 120, 193)",
122
+ "show": false
123
+ },
124
+ "tableColumn": "",
125
+ "targets": [
126
+ {
127
+ "expr": "process_uptime_seconds{application=\"$application\", instance=\"$instance\"}",
128
+ "format": "time_series",
129
+ "intervalFactor": 2,
130
+ "legendFormat": "",
131
+ "metric": "",
132
+ "refId": "A",
133
+ "step": 14400
134
+ }
135
+ ],
136
+ "thresholds": "",
137
+ "title": "Uptime",
138
+ "type": "singlestat",
139
+ "valueFontSize": "80%",
140
+ "valueMaps": [
141
+ {
142
+ "op": "=",
143
+ "text": "N/A",
144
+ "value": "null"
145
+ }
146
+ ],
147
+ "valueName": "current"
148
+ },
149
+ {
150
+ "cacheTimeout": null,
151
+ "colorBackground": false,
152
+ "colorValue": true,
153
+ "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"],
154
+ "datasource": "Prometheus",
155
+ "decimals": null,
156
+ "editable": true,
157
+ "error": false,
158
+ "format": "dateTimeAsIso",
159
+ "gauge": {
160
+ "maxValue": 100,
161
+ "minValue": 0,
162
+ "show": false,
163
+ "thresholdLabels": false,
164
+ "thresholdMarkers": true
165
+ },
166
+ "gridPos": {
167
+ "h": 3,
168
+ "w": 6,
169
+ "x": 6,
170
+ "y": 4
171
+ },
172
+ "height": "",
173
+ "id": 92,
174
+ "interval": null,
175
+ "links": [],
176
+ "mappingType": 1,
177
+ "mappingTypes": [
178
+ {
179
+ "name": "value to text",
180
+ "value": 1
181
+ },
182
+ {
183
+ "name": "range to text",
184
+ "value": 2
185
+ }
186
+ ],
187
+ "maxDataPoints": 100,
188
+ "nullPointMode": "connected",
189
+ "nullText": null,
190
+ "postfix": "",
191
+ "postfixFontSize": "50%",
192
+ "prefix": "",
193
+ "prefixFontSize": "70%",
194
+ "rangeMaps": [
195
+ {
196
+ "from": "null",
197
+ "text": "N/A",
198
+ "to": "null"
199
+ }
200
+ ],
201
+ "sparkline": {
202
+ "fillColor": "rgba(31, 118, 189, 0.18)",
203
+ "full": false,
204
+ "lineColor": "rgb(31, 120, 193)",
205
+ "show": false
206
+ },
207
+ "tableColumn": "",
208
+ "targets": [
209
+ {
210
+ "expr": "process_start_time_seconds{application=\"$application\", instance=\"$instance\"}*1000",
211
+ "format": "time_series",
212
+ "intervalFactor": 2,
213
+ "legendFormat": "",
214
+ "metric": "",
215
+ "refId": "A",
216
+ "step": 14400
217
+ }
218
+ ],
219
+ "thresholds": "",
220
+ "title": "Start time",
221
+ "type": "singlestat",
222
+ "valueFontSize": "70%",
223
+ "valueMaps": [
224
+ {
225
+ "op": "=",
226
+ "text": "N/A",
227
+ "value": "null"
228
+ }
229
+ ],
230
+ "valueName": "current"
231
+ },
232
+ {
233
+ "cacheTimeout": null,
234
+ "colorBackground": false,
235
+ "colorValue": true,
236
+ "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "rgba(245, 54, 54, 0.9)"],
237
+ "datasource": "Prometheus",
238
+ "decimals": 2,
239
+ "editable": true,
240
+ "error": false,
241
+ "format": "percent",
242
+ "gauge": {
243
+ "maxValue": 100,
244
+ "minValue": 0,
245
+ "show": false,
246
+ "thresholdLabels": false,
247
+ "thresholdMarkers": true
248
+ },
249
+ "gridPos": {
250
+ "h": 3,
251
+ "w": 6,
252
+ "x": 12,
253
+ "y": 4
254
+ },
255
+ "id": 65,
256
+ "interval": null,
257
+ "links": [],
258
+ "mappingType": 1,
259
+ "mappingTypes": [
260
+ {
261
+ "name": "value to text",
262
+ "value": 1
263
+ },
264
+ {
265
+ "name": "range to text",
266
+ "value": 2
267
+ }
268
+ ],
269
+ "maxDataPoints": 100,
270
+ "nullPointMode": "connected",
271
+ "nullText": null,
272
+ "postfix": "",
273
+ "postfixFontSize": "50%",
274
+ "prefix": "",
275
+ "prefixFontSize": "70%",
276
+ "rangeMaps": [
277
+ {
278
+ "from": "null",
279
+ "text": "N/A",
280
+ "to": "null"
281
+ }
282
+ ],
283
+ "sparkline": {
284
+ "fillColor": "rgba(31, 118, 189, 0.18)",
285
+ "full": false,
286
+ "lineColor": "rgb(31, 120, 193)",
287
+ "show": false
288
+ },
289
+ "tableColumn": "",
290
+ "targets": [
291
+ {
292
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"heap\"})",
293
+ "format": "time_series",
294
+ "intervalFactor": 2,
295
+ "legendFormat": "",
296
+ "refId": "A",
297
+ "step": 14400
298
+ }
299
+ ],
300
+ "thresholds": "70,90",
301
+ "title": "Heap used",
302
+ "type": "singlestat",
303
+ "valueFontSize": "80%",
304
+ "valueMaps": [
305
+ {
306
+ "op": "=",
307
+ "text": "N/A",
308
+ "value": "null"
309
+ }
310
+ ],
311
+ "valueName": "current"
312
+ },
313
+ {
314
+ "cacheTimeout": null,
315
+ "colorBackground": false,
316
+ "colorValue": true,
317
+ "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "rgba(245, 54, 54, 0.9)"],
318
+ "datasource": "Prometheus",
319
+ "decimals": 2,
320
+ "editable": true,
321
+ "error": false,
322
+ "format": "percent",
323
+ "gauge": {
324
+ "maxValue": 100,
325
+ "minValue": 0,
326
+ "show": false,
327
+ "thresholdLabels": false,
328
+ "thresholdMarkers": true
329
+ },
330
+ "gridPos": {
331
+ "h": 3,
332
+ "w": 6,
333
+ "x": 18,
334
+ "y": 4
335
+ },
336
+ "id": 75,
337
+ "interval": null,
338
+ "links": [],
339
+ "mappingType": 2,
340
+ "mappingTypes": [
341
+ {
342
+ "name": "value to text",
343
+ "value": 1
344
+ },
345
+ {
346
+ "name": "range to text",
347
+ "value": 2
348
+ }
349
+ ],
350
+ "maxDataPoints": 100,
351
+ "nullPointMode": "connected",
352
+ "nullText": null,
353
+ "postfix": "",
354
+ "postfixFontSize": "50%",
355
+ "prefix": "",
356
+ "prefixFontSize": "70%",
357
+ "rangeMaps": [
358
+ {
359
+ "from": "null",
360
+ "text": "N/A",
361
+ "to": "null"
362
+ },
363
+ {
364
+ "from": "-99999999999999999999999999999999",
365
+ "text": "N/A",
366
+ "to": "0"
367
+ }
368
+ ],
369
+ "sparkline": {
370
+ "fillColor": "rgba(31, 118, 189, 0.18)",
371
+ "full": false,
372
+ "lineColor": "rgb(31, 120, 193)",
373
+ "show": false
374
+ },
375
+ "tableColumn": "",
376
+ "targets": [
377
+ {
378
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"nonheap\"})",
379
+ "format": "time_series",
380
+ "intervalFactor": 2,
381
+ "legendFormat": "",
382
+ "refId": "A",
383
+ "step": 14400
384
+ }
385
+ ],
386
+ "thresholds": "70,90",
387
+ "title": "Non-Heap used",
388
+ "type": "singlestat",
389
+ "valueFontSize": "80%",
390
+ "valueMaps": [
391
+ {
392
+ "op": "=",
393
+ "text": "N/A",
394
+ "value": "null"
395
+ },
396
+ {
397
+ "op": "=",
398
+ "text": "x",
399
+ "value": ""
400
+ }
401
+ ],
402
+ "valueName": "current"
403
+ },
404
+ {
405
+ "collapsed": false,
406
+ "gridPos": {
407
+ "h": 1,
408
+ "w": 24,
409
+ "x": 0,
410
+ "y": 7
411
+ },
412
+ "id": 126,
413
+ "panels": [],
414
+ "repeat": null,
415
+ "title": "I/O Overview",
416
+ "type": "row"
417
+ },
418
+ {
419
+ "aliasColors": {},
420
+ "bars": false,
421
+ "dashLength": 10,
422
+ "dashes": false,
423
+ "datasource": "Prometheus",
424
+ "fill": 1,
425
+ "gridPos": {
426
+ "h": 7,
427
+ "w": 8,
428
+ "x": 0,
429
+ "y": 8
430
+ },
431
+ "id": 111,
432
+ "legend": {
433
+ "avg": false,
434
+ "current": true,
435
+ "max": false,
436
+ "min": false,
437
+ "show": true,
438
+ "total": false,
439
+ "values": true
440
+ },
441
+ "lines": true,
442
+ "linewidth": 1,
443
+ "links": [],
444
+ "nullPointMode": "null",
445
+ "paceLength": 10,
446
+ "percentage": false,
447
+ "pointradius": 5,
448
+ "points": false,
449
+ "renderer": "flot",
450
+ "seriesOverrides": [],
451
+ "spaceLength": 10,
452
+ "stack": false,
453
+ "steppedLine": false,
454
+ "targets": [
455
+ {
456
+ "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\"}[1m]))",
457
+ "format": "time_series",
458
+ "intervalFactor": 1,
459
+ "legendFormat": "HTTP",
460
+ "refId": "A"
461
+ }
462
+ ],
463
+ "thresholds": [],
464
+ "timeFrom": null,
465
+ "timeRegions": [],
466
+ "timeShift": null,
467
+ "title": "Rate",
468
+ "tooltip": {
469
+ "shared": true,
470
+ "sort": 0,
471
+ "value_type": "individual"
472
+ },
473
+ "type": "graph",
474
+ "xaxis": {
475
+ "buckets": null,
476
+ "mode": "time",
477
+ "name": null,
478
+ "show": true,
479
+ "values": []
480
+ },
481
+ "yaxes": [
482
+ {
483
+ "decimals": null,
484
+ "format": "ops",
485
+ "label": null,
486
+ "logBase": 1,
487
+ "max": null,
488
+ "min": "0",
489
+ "show": true
490
+ },
491
+ {
492
+ "format": "short",
493
+ "label": null,
494
+ "logBase": 1,
495
+ "max": null,
496
+ "min": null,
497
+ "show": true
498
+ }
499
+ ],
500
+ "yaxis": {
501
+ "align": false,
502
+ "alignLevel": null
503
+ }
504
+ },
505
+ {
506
+ "aliasColors": {
507
+ "HTTP": "#890f02",
508
+ "HTTP - 5xx": "#bf1b00"
509
+ },
510
+ "bars": false,
511
+ "dashLength": 10,
512
+ "dashes": false,
513
+ "datasource": "Prometheus",
514
+ "fill": 1,
515
+ "gridPos": {
516
+ "h": 7,
517
+ "w": 8,
518
+ "x": 8,
519
+ "y": 8
520
+ },
521
+ "id": 112,
522
+ "legend": {
523
+ "avg": false,
524
+ "current": true,
525
+ "max": false,
526
+ "min": false,
527
+ "show": true,
528
+ "total": false,
529
+ "values": true
530
+ },
531
+ "lines": true,
532
+ "linewidth": 1,
533
+ "links": [],
534
+ "nullPointMode": "null",
535
+ "paceLength": 10,
536
+ "percentage": false,
537
+ "pointradius": 5,
538
+ "points": false,
539
+ "renderer": "flot",
540
+ "seriesOverrides": [],
541
+ "spaceLength": 10,
542
+ "stack": false,
543
+ "steppedLine": false,
544
+ "targets": [
545
+ {
546
+ "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status=~\"5..\"}[1m]))",
547
+ "format": "time_series",
548
+ "intervalFactor": 1,
549
+ "legendFormat": "HTTP - 5xx",
550
+ "refId": "A"
551
+ }
552
+ ],
553
+ "thresholds": [],
554
+ "timeFrom": null,
555
+ "timeRegions": [],
556
+ "timeShift": null,
557
+ "title": "Errors",
558
+ "tooltip": {
559
+ "shared": true,
560
+ "sort": 0,
561
+ "value_type": "individual"
562
+ },
563
+ "type": "graph",
564
+ "xaxis": {
565
+ "buckets": null,
566
+ "mode": "time",
567
+ "name": null,
568
+ "show": true,
569
+ "values": []
570
+ },
571
+ "yaxes": [
572
+ {
573
+ "decimals": null,
574
+ "format": "ops",
575
+ "label": null,
576
+ "logBase": 1,
577
+ "max": null,
578
+ "min": "0",
579
+ "show": true
580
+ },
581
+ {
582
+ "format": "short",
583
+ "label": null,
584
+ "logBase": 1,
585
+ "max": null,
586
+ "min": null,
587
+ "show": true
588
+ }
589
+ ],
590
+ "yaxis": {
591
+ "align": false,
592
+ "alignLevel": null
593
+ }
594
+ },
595
+ {
596
+ "aliasColors": {},
597
+ "bars": false,
598
+ "dashLength": 10,
599
+ "dashes": false,
600
+ "datasource": "Prometheus",
601
+ "fill": 1,
602
+ "gridPos": {
603
+ "h": 7,
604
+ "w": 8,
605
+ "x": 16,
606
+ "y": 8
607
+ },
608
+ "id": 113,
609
+ "legend": {
610
+ "avg": false,
611
+ "current": true,
612
+ "max": false,
613
+ "min": false,
614
+ "show": true,
615
+ "total": false,
616
+ "values": true
617
+ },
618
+ "lines": true,
619
+ "linewidth": 1,
620
+ "links": [],
621
+ "nullPointMode": "null",
622
+ "paceLength": 10,
623
+ "percentage": false,
624
+ "pointradius": 5,
625
+ "points": false,
626
+ "renderer": "flot",
627
+ "seriesOverrides": [],
628
+ "spaceLength": 10,
629
+ "stack": false,
630
+ "steppedLine": false,
631
+ "targets": [
632
+ {
633
+ "expr": "sum(rate(http_server_requests_seconds_sum{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))/sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))",
634
+ "format": "time_series",
635
+ "hide": false,
636
+ "intervalFactor": 1,
637
+ "legendFormat": "HTTP - AVG",
638
+ "refId": "A"
639
+ },
640
+ {
641
+ "expr": "max(http_server_requests_seconds_max{application=\"$application\", instance=\"$instance\", status!~\"5..\"})",
642
+ "format": "time_series",
643
+ "hide": false,
644
+ "intervalFactor": 1,
645
+ "legendFormat": "HTTP - MAX",
646
+ "refId": "B"
647
+ }
648
+ ],
649
+ "thresholds": [],
650
+ "timeFrom": null,
651
+ "timeRegions": [],
652
+ "timeShift": null,
653
+ "title": "Duration",
654
+ "tooltip": {
655
+ "shared": true,
656
+ "sort": 0,
657
+ "value_type": "individual"
658
+ },
659
+ "type": "graph",
660
+ "xaxis": {
661
+ "buckets": null,
662
+ "mode": "time",
663
+ "name": null,
664
+ "show": true,
665
+ "values": []
666
+ },
667
+ "yaxes": [
668
+ {
669
+ "format": "s",
670
+ "label": null,
671
+ "logBase": 1,
672
+ "max": null,
673
+ "min": "0",
674
+ "show": true
675
+ },
676
+ {
677
+ "format": "short",
678
+ "label": null,
679
+ "logBase": 1,
680
+ "max": null,
681
+ "min": null,
682
+ "show": true
683
+ }
684
+ ],
685
+ "yaxis": {
686
+ "align": false,
687
+ "alignLevel": null
688
+ }
689
+ },
690
+ {
691
+ "collapsed": false,
692
+ "gridPos": {
693
+ "h": 1,
694
+ "w": 24,
695
+ "x": 0,
696
+ "y": 15
697
+ },
698
+ "id": 127,
699
+ "panels": [],
700
+ "repeat": null,
701
+ "title": "JVM Memory",
702
+ "type": "row"
703
+ },
704
+ {
705
+ "aliasColors": {},
706
+ "bars": false,
707
+ "dashLength": 10,
708
+ "dashes": false,
709
+ "datasource": "Prometheus",
710
+ "editable": true,
711
+ "error": false,
712
+ "fill": 1,
713
+ "grid": {
714
+ "leftLogBase": 1,
715
+ "leftMax": null,
716
+ "leftMin": null,
717
+ "rightLogBase": 1,
718
+ "rightMax": null,
719
+ "rightMin": null
720
+ },
721
+ "gridPos": {
722
+ "h": 7,
723
+ "w": 8,
724
+ "x": 0,
725
+ "y": 16
726
+ },
727
+ "id": 24,
728
+ "legend": {
729
+ "avg": false,
730
+ "current": true,
731
+ "max": true,
732
+ "min": false,
733
+ "show": true,
734
+ "total": false,
735
+ "values": true
736
+ },
737
+ "lines": true,
738
+ "linewidth": 1,
739
+ "links": [],
740
+ "nullPointMode": "null",
741
+ "paceLength": 10,
742
+ "percentage": false,
743
+ "pointradius": 5,
744
+ "points": false,
745
+ "renderer": "flot",
746
+ "seriesOverrides": [],
747
+ "spaceLength": 10,
748
+ "stack": false,
749
+ "steppedLine": false,
750
+ "targets": [
751
+ {
752
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})",
753
+ "format": "time_series",
754
+ "intervalFactor": 2,
755
+ "legendFormat": "used",
756
+ "metric": "",
757
+ "refId": "A",
758
+ "step": 2400
759
+ },
760
+ {
761
+ "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})",
762
+ "format": "time_series",
763
+ "intervalFactor": 2,
764
+ "legendFormat": "committed",
765
+ "refId": "B",
766
+ "step": 2400
767
+ },
768
+ {
769
+ "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})",
770
+ "format": "time_series",
771
+ "intervalFactor": 2,
772
+ "legendFormat": "max",
773
+ "refId": "C",
774
+ "step": 2400
775
+ }
776
+ ],
777
+ "thresholds": [],
778
+ "timeFrom": null,
779
+ "timeRegions": [],
780
+ "timeShift": null,
781
+ "title": "JVM Heap",
782
+ "tooltip": {
783
+ "msResolution": false,
784
+ "shared": true,
785
+ "sort": 0,
786
+ "value_type": "cumulative"
787
+ },
788
+ "type": "graph",
789
+ "x-axis": true,
790
+ "xaxis": {
791
+ "buckets": null,
792
+ "mode": "time",
793
+ "name": null,
794
+ "show": true,
795
+ "values": []
796
+ },
797
+ "y-axis": true,
798
+ "y_formats": ["mbytes", "short"],
799
+ "yaxes": [
800
+ {
801
+ "format": "bytes",
802
+ "label": null,
803
+ "logBase": 1,
804
+ "max": null,
805
+ "min": 0,
806
+ "show": true
807
+ },
808
+ {
809
+ "format": "short",
810
+ "label": null,
811
+ "logBase": 1,
812
+ "max": null,
813
+ "min": null,
814
+ "show": true
815
+ }
816
+ ],
817
+ "yaxis": {
818
+ "align": false,
819
+ "alignLevel": null
820
+ }
821
+ },
822
+ {
823
+ "aliasColors": {},
824
+ "bars": false,
825
+ "dashLength": 10,
826
+ "dashes": false,
827
+ "datasource": "Prometheus",
828
+ "editable": true,
829
+ "error": false,
830
+ "fill": 1,
831
+ "grid": {
832
+ "leftLogBase": 1,
833
+ "leftMax": null,
834
+ "leftMin": null,
835
+ "rightLogBase": 1,
836
+ "rightMax": null,
837
+ "rightMin": null
838
+ },
839
+ "gridPos": {
840
+ "h": 7,
841
+ "w": 8,
842
+ "x": 8,
843
+ "y": 16
844
+ },
845
+ "id": 25,
846
+ "legend": {
847
+ "avg": false,
848
+ "current": true,
849
+ "max": true,
850
+ "min": false,
851
+ "show": true,
852
+ "total": false,
853
+ "values": true
854
+ },
855
+ "lines": true,
856
+ "linewidth": 1,
857
+ "links": [],
858
+ "nullPointMode": "null",
859
+ "paceLength": 10,
860
+ "percentage": false,
861
+ "pointradius": 5,
862
+ "points": false,
863
+ "renderer": "flot",
864
+ "seriesOverrides": [],
865
+ "spaceLength": 10,
866
+ "stack": false,
867
+ "steppedLine": false,
868
+ "targets": [
869
+ {
870
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})",
871
+ "format": "time_series",
872
+ "interval": "",
873
+ "intervalFactor": 2,
874
+ "legendFormat": "used",
875
+ "metric": "",
876
+ "refId": "A",
877
+ "step": 2400
878
+ },
879
+ {
880
+ "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})",
881
+ "format": "time_series",
882
+ "intervalFactor": 2,
883
+ "legendFormat": "committed",
884
+ "refId": "B",
885
+ "step": 2400
886
+ },
887
+ {
888
+ "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})",
889
+ "format": "time_series",
890
+ "intervalFactor": 2,
891
+ "legendFormat": "max",
892
+ "refId": "C",
893
+ "step": 2400
894
+ }
895
+ ],
896
+ "thresholds": [],
897
+ "timeFrom": null,
898
+ "timeRegions": [],
899
+ "timeShift": null,
900
+ "title": "JVM Non-Heap",
901
+ "tooltip": {
902
+ "msResolution": false,
903
+ "shared": true,
904
+ "sort": 0,
905
+ "value_type": "cumulative"
906
+ },
907
+ "type": "graph",
908
+ "x-axis": true,
909
+ "xaxis": {
910
+ "buckets": null,
911
+ "mode": "time",
912
+ "name": null,
913
+ "show": true,
914
+ "values": []
915
+ },
916
+ "y-axis": true,
917
+ "y_formats": ["mbytes", "short"],
918
+ "yaxes": [
919
+ {
920
+ "format": "bytes",
921
+ "label": null,
922
+ "logBase": 1,
923
+ "max": null,
924
+ "min": 0,
925
+ "show": true
926
+ },
927
+ {
928
+ "format": "short",
929
+ "label": null,
930
+ "logBase": 1,
931
+ "max": null,
932
+ "min": null,
933
+ "show": true
934
+ }
935
+ ],
936
+ "yaxis": {
937
+ "align": false,
938
+ "alignLevel": null
939
+ }
940
+ },
941
+ {
942
+ "aliasColors": {},
943
+ "bars": false,
944
+ "dashLength": 10,
945
+ "dashes": false,
946
+ "datasource": "Prometheus",
947
+ "editable": true,
948
+ "error": false,
949
+ "fill": 1,
950
+ "grid": {
951
+ "leftLogBase": 1,
952
+ "leftMax": null,
953
+ "leftMin": null,
954
+ "rightLogBase": 1,
955
+ "rightMax": null,
956
+ "rightMin": null
957
+ },
958
+ "gridPos": {
959
+ "h": 7,
960
+ "w": 8,
961
+ "x": 16,
962
+ "y": 16
963
+ },
964
+ "id": 26,
965
+ "legend": {
966
+ "alignAsTable": false,
967
+ "avg": false,
968
+ "current": true,
969
+ "max": true,
970
+ "min": false,
971
+ "show": true,
972
+ "total": false,
973
+ "values": true
974
+ },
975
+ "lines": true,
976
+ "linewidth": 1,
977
+ "links": [],
978
+ "nullPointMode": "null",
979
+ "paceLength": 10,
980
+ "percentage": false,
981
+ "pointradius": 5,
982
+ "points": false,
983
+ "renderer": "flot",
984
+ "seriesOverrides": [],
985
+ "spaceLength": 10,
986
+ "stack": false,
987
+ "steppedLine": false,
988
+ "targets": [
989
+ {
990
+ "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\"})",
991
+ "format": "time_series",
992
+ "intervalFactor": 2,
993
+ "legendFormat": "used",
994
+ "metric": "",
995
+ "refId": "A",
996
+ "step": 2400
997
+ },
998
+ {
999
+ "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\"})",
1000
+ "format": "time_series",
1001
+ "intervalFactor": 2,
1002
+ "legendFormat": "committed",
1003
+ "refId": "B",
1004
+ "step": 2400
1005
+ },
1006
+ {
1007
+ "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\"})",
1008
+ "format": "time_series",
1009
+ "intervalFactor": 2,
1010
+ "legendFormat": "max",
1011
+ "refId": "C",
1012
+ "step": 2400
1013
+ },
1014
+ {
1015
+ "expr": "process_memory_vss_bytes{application=\"$application\", instance=\"$instance\"}",
1016
+ "format": "time_series",
1017
+ "hide": true,
1018
+ "intervalFactor": 2,
1019
+ "legendFormat": "vss",
1020
+ "metric": "",
1021
+ "refId": "D",
1022
+ "step": 2400
1023
+ },
1024
+ {
1025
+ "expr": "process_memory_rss_bytes{application=\"$application\", instance=\"$instance\"}",
1026
+ "format": "time_series",
1027
+ "intervalFactor": 2,
1028
+ "legendFormat": "rss",
1029
+ "refId": "E",
1030
+ "step": 2400
1031
+ },
1032
+ {
1033
+ "expr": "process_memory_pss_bytes{application=\"$application\", instance=\"$instance\"}",
1034
+ "format": "time_series",
1035
+ "intervalFactor": 2,
1036
+ "legendFormat": "pss",
1037
+ "refId": "F",
1038
+ "step": 2400
1039
+ },
1040
+ {
1041
+ "expr": "process_memory_swap_bytes{application=\"$application\", instance=\"$instance\"}",
1042
+ "format": "time_series",
1043
+ "intervalFactor": 2,
1044
+ "legendFormat": "swap",
1045
+ "refId": "G",
1046
+ "step": 2400
1047
+ },
1048
+ {
1049
+ "expr": "process_memory_swappss_bytes{application=\"$application\", instance=\"$instance\"}",
1050
+ "format": "time_series",
1051
+ "intervalFactor": 2,
1052
+ "legendFormat": "swappss",
1053
+ "refId": "H",
1054
+ "step": 2400
1055
+ },
1056
+ {
1057
+ "expr": "process_memory_pss_bytes{application=\"$application\", instance=\"$instance\"} + process_memory_swap_bytes{application=\"$application\", instance=\"$instance\"}",
1058
+ "format": "time_series",
1059
+ "intervalFactor": 2,
1060
+ "legendFormat": "phys (pss+swap)",
1061
+ "refId": "I",
1062
+ "step": 2400
1063
+ }
1064
+ ],
1065
+ "thresholds": [],
1066
+ "timeFrom": null,
1067
+ "timeRegions": [],
1068
+ "timeShift": null,
1069
+ "title": "JVM Total",
1070
+ "tooltip": {
1071
+ "msResolution": false,
1072
+ "shared": true,
1073
+ "sort": 0,
1074
+ "value_type": "cumulative"
1075
+ },
1076
+ "type": "graph",
1077
+ "x-axis": true,
1078
+ "xaxis": {
1079
+ "buckets": null,
1080
+ "mode": "time",
1081
+ "name": null,
1082
+ "show": true,
1083
+ "values": []
1084
+ },
1085
+ "y-axis": true,
1086
+ "y_formats": ["mbytes", "short"],
1087
+ "yaxes": [
1088
+ {
1089
+ "format": "bytes",
1090
+ "label": "",
1091
+ "logBase": 1,
1092
+ "max": null,
1093
+ "min": 0,
1094
+ "show": true
1095
+ },
1096
+ {
1097
+ "format": "short",
1098
+ "label": null,
1099
+ "logBase": 1,
1100
+ "max": null,
1101
+ "min": null,
1102
+ "show": true
1103
+ }
1104
+ ],
1105
+ "yaxis": {
1106
+ "align": false,
1107
+ "alignLevel": null
1108
+ }
1109
+ },
1110
+ {
1111
+ "collapsed": false,
1112
+ "gridPos": {
1113
+ "h": 1,
1114
+ "w": 24,
1115
+ "x": 0,
1116
+ "y": 23
1117
+ },
1118
+ "id": 128,
1119
+ "panels": [],
1120
+ "repeat": null,
1121
+ "title": "JVM Misc",
1122
+ "type": "row"
1123
+ },
1124
+ {
1125
+ "aliasColors": {},
1126
+ "bars": false,
1127
+ "dashLength": 10,
1128
+ "dashes": false,
1129
+ "datasource": "Prometheus",
1130
+ "editable": true,
1131
+ "error": false,
1132
+ "fill": 1,
1133
+ "grid": {
1134
+ "leftLogBase": 1,
1135
+ "leftMax": null,
1136
+ "leftMin": null,
1137
+ "rightLogBase": 1,
1138
+ "rightMax": null,
1139
+ "rightMin": null
1140
+ },
1141
+ "gridPos": {
1142
+ "h": 7,
1143
+ "w": 6,
1144
+ "x": 0,
1145
+ "y": 24
1146
+ },
1147
+ "id": 106,
1148
+ "legend": {
1149
+ "avg": false,
1150
+ "current": true,
1151
+ "max": true,
1152
+ "min": false,
1153
+ "show": true,
1154
+ "total": false,
1155
+ "values": true
1156
+ },
1157
+ "lines": true,
1158
+ "linewidth": 1,
1159
+ "links": [],
1160
+ "nullPointMode": "null",
1161
+ "paceLength": 10,
1162
+ "percentage": false,
1163
+ "pointradius": 5,
1164
+ "points": false,
1165
+ "renderer": "flot",
1166
+ "seriesOverrides": [],
1167
+ "spaceLength": 10,
1168
+ "stack": false,
1169
+ "steppedLine": false,
1170
+ "targets": [
1171
+ {
1172
+ "expr": "system_cpu_usage{application=\"$application\", instance=\"$instance\"}",
1173
+ "format": "time_series",
1174
+ "hide": false,
1175
+ "intervalFactor": 1,
1176
+ "legendFormat": "system",
1177
+ "metric": "",
1178
+ "refId": "A",
1179
+ "step": 2400
1180
+ },
1181
+ {
1182
+ "expr": "process_cpu_usage{application=\"$application\", instance=\"$instance\"}",
1183
+ "format": "time_series",
1184
+ "hide": false,
1185
+ "intervalFactor": 1,
1186
+ "legendFormat": "process",
1187
+ "refId": "B"
1188
+ },
1189
+ {
1190
+ "expr": "avg_over_time(process_cpu_usage{application=\"$application\", instance=\"$instance\"}[1h])",
1191
+ "format": "time_series",
1192
+ "hide": false,
1193
+ "intervalFactor": 1,
1194
+ "legendFormat": "process-1h",
1195
+ "refId": "C"
1196
+ }
1197
+ ],
1198
+ "thresholds": [],
1199
+ "timeFrom": null,
1200
+ "timeRegions": [],
1201
+ "timeShift": null,
1202
+ "title": "CPU",
1203
+ "tooltip": {
1204
+ "msResolution": false,
1205
+ "shared": true,
1206
+ "sort": 0,
1207
+ "value_type": "cumulative"
1208
+ },
1209
+ "type": "graph",
1210
+ "x-axis": true,
1211
+ "xaxis": {
1212
+ "buckets": null,
1213
+ "mode": "time",
1214
+ "name": null,
1215
+ "show": true,
1216
+ "values": []
1217
+ },
1218
+ "y-axis": true,
1219
+ "y_formats": ["short", "short"],
1220
+ "yaxes": [
1221
+ {
1222
+ "decimals": 1,
1223
+ "format": "percentunit",
1224
+ "label": "",
1225
+ "logBase": 1,
1226
+ "max": "1",
1227
+ "min": 0,
1228
+ "show": true
1229
+ },
1230
+ {
1231
+ "format": "short",
1232
+ "label": null,
1233
+ "logBase": 1,
1234
+ "max": null,
1235
+ "min": null,
1236
+ "show": true
1237
+ }
1238
+ ],
1239
+ "yaxis": {
1240
+ "align": false,
1241
+ "alignLevel": null
1242
+ }
1243
+ },
1244
+ {
1245
+ "aliasColors": {},
1246
+ "bars": false,
1247
+ "dashLength": 10,
1248
+ "dashes": false,
1249
+ "datasource": "Prometheus",
1250
+ "editable": true,
1251
+ "error": false,
1252
+ "fill": 1,
1253
+ "grid": {
1254
+ "leftLogBase": 1,
1255
+ "leftMax": null,
1256
+ "leftMin": null,
1257
+ "rightLogBase": 1,
1258
+ "rightMax": null,
1259
+ "rightMin": null
1260
+ },
1261
+ "gridPos": {
1262
+ "h": 7,
1263
+ "w": 6,
1264
+ "x": 6,
1265
+ "y": 24
1266
+ },
1267
+ "id": 93,
1268
+ "legend": {
1269
+ "avg": false,
1270
+ "current": true,
1271
+ "max": true,
1272
+ "min": false,
1273
+ "show": true,
1274
+ "total": false,
1275
+ "values": true
1276
+ },
1277
+ "lines": true,
1278
+ "linewidth": 1,
1279
+ "links": [],
1280
+ "nullPointMode": "null",
1281
+ "paceLength": 10,
1282
+ "percentage": false,
1283
+ "pointradius": 5,
1284
+ "points": false,
1285
+ "renderer": "flot",
1286
+ "seriesOverrides": [],
1287
+ "spaceLength": 10,
1288
+ "stack": false,
1289
+ "steppedLine": false,
1290
+ "targets": [
1291
+ {
1292
+ "expr": "system_load_average_1m{application=\"$application\", instance=\"$instance\"}",
1293
+ "format": "time_series",
1294
+ "intervalFactor": 2,
1295
+ "legendFormat": "system-1m",
1296
+ "metric": "",
1297
+ "refId": "A",
1298
+ "step": 2400
1299
+ },
1300
+ {
1301
+ "expr": "",
1302
+ "format": "time_series",
1303
+ "intervalFactor": 2,
1304
+ "refId": "B"
1305
+ },
1306
+ {
1307
+ "expr": "system_cpu_count{application=\"$application\", instance=\"$instance\"}",
1308
+ "format": "time_series",
1309
+ "intervalFactor": 2,
1310
+ "legendFormat": "cpu",
1311
+ "refId": "C"
1312
+ }
1313
+ ],
1314
+ "thresholds": [],
1315
+ "timeFrom": null,
1316
+ "timeRegions": [],
1317
+ "timeShift": null,
1318
+ "title": "Load",
1319
+ "tooltip": {
1320
+ "msResolution": false,
1321
+ "shared": true,
1322
+ "sort": 0,
1323
+ "value_type": "cumulative"
1324
+ },
1325
+ "type": "graph",
1326
+ "x-axis": true,
1327
+ "xaxis": {
1328
+ "buckets": null,
1329
+ "mode": "time",
1330
+ "name": null,
1331
+ "show": true,
1332
+ "values": []
1333
+ },
1334
+ "y-axis": true,
1335
+ "y_formats": ["short", "short"],
1336
+ "yaxes": [
1337
+ {
1338
+ "decimals": 1,
1339
+ "format": "short",
1340
+ "label": "",
1341
+ "logBase": 1,
1342
+ "max": null,
1343
+ "min": 0,
1344
+ "show": true
1345
+ },
1346
+ {
1347
+ "format": "short",
1348
+ "label": null,
1349
+ "logBase": 1,
1350
+ "max": null,
1351
+ "min": null,
1352
+ "show": true
1353
+ }
1354
+ ],
1355
+ "yaxis": {
1356
+ "align": false,
1357
+ "alignLevel": null
1358
+ }
1359
+ },
1360
+ {
1361
+ "aliasColors": {},
1362
+ "bars": false,
1363
+ "dashLength": 10,
1364
+ "dashes": false,
1365
+ "datasource": "Prometheus",
1366
+ "editable": true,
1367
+ "error": false,
1368
+ "fill": 1,
1369
+ "grid": {
1370
+ "leftLogBase": 1,
1371
+ "leftMax": null,
1372
+ "leftMin": null,
1373
+ "rightLogBase": 1,
1374
+ "rightMax": null,
1375
+ "rightMin": null
1376
+ },
1377
+ "gridPos": {
1378
+ "h": 7,
1379
+ "w": 6,
1380
+ "x": 12,
1381
+ "y": 24
1382
+ },
1383
+ "id": 32,
1384
+ "legend": {
1385
+ "avg": false,
1386
+ "current": true,
1387
+ "max": true,
1388
+ "min": false,
1389
+ "show": true,
1390
+ "total": false,
1391
+ "values": true
1392
+ },
1393
+ "lines": true,
1394
+ "linewidth": 1,
1395
+ "links": [],
1396
+ "nullPointMode": "null",
1397
+ "paceLength": 10,
1398
+ "percentage": false,
1399
+ "pointradius": 5,
1400
+ "points": false,
1401
+ "renderer": "flot",
1402
+ "seriesOverrides": [],
1403
+ "spaceLength": 10,
1404
+ "stack": false,
1405
+ "steppedLine": false,
1406
+ "targets": [
1407
+ {
1408
+ "expr": "jvm_threads_live{application=\"$application\", instance=\"$instance\"} or jvm_threads_live_threads{application=\"$application\", instance=\"$instance\"}",
1409
+ "format": "time_series",
1410
+ "intervalFactor": 2,
1411
+ "legendFormat": "live",
1412
+ "metric": "",
1413
+ "refId": "A",
1414
+ "step": 2400
1415
+ },
1416
+ {
1417
+ "expr": "jvm_threads_daemon{application=\"$application\", instance=\"$instance\"} or jvm_threads_daemon_threads{application=\"$application\", instance=\"$instance\"}",
1418
+ "format": "time_series",
1419
+ "intervalFactor": 2,
1420
+ "legendFormat": "daemon",
1421
+ "metric": "",
1422
+ "refId": "B",
1423
+ "step": 2400
1424
+ },
1425
+ {
1426
+ "expr": "jvm_threads_peak{application=\"$application\", instance=\"$instance\"} or jvm_threads_peak_threads{application=\"$application\", instance=\"$instance\"}",
1427
+ "format": "time_series",
1428
+ "intervalFactor": 2,
1429
+ "legendFormat": "peak",
1430
+ "refId": "C",
1431
+ "step": 2400
1432
+ },
1433
+ {
1434
+ "expr": "process_threads{application=\"$application\", instance=\"$instance\"}",
1435
+ "format": "time_series",
1436
+ "interval": "",
1437
+ "intervalFactor": 2,
1438
+ "legendFormat": "process",
1439
+ "refId": "D",
1440
+ "step": 2400
1441
+ }
1442
+ ],
1443
+ "thresholds": [],
1444
+ "timeFrom": null,
1445
+ "timeRegions": [],
1446
+ "timeShift": null,
1447
+ "title": "Threads",
1448
+ "tooltip": {
1449
+ "msResolution": false,
1450
+ "shared": true,
1451
+ "sort": 0,
1452
+ "value_type": "cumulative"
1453
+ },
1454
+ "type": "graph",
1455
+ "x-axis": true,
1456
+ "xaxis": {
1457
+ "buckets": null,
1458
+ "mode": "time",
1459
+ "name": null,
1460
+ "show": true,
1461
+ "values": []
1462
+ },
1463
+ "y-axis": true,
1464
+ "y_formats": ["short", "short"],
1465
+ "yaxes": [
1466
+ {
1467
+ "decimals": 0,
1468
+ "format": "short",
1469
+ "label": null,
1470
+ "logBase": 1,
1471
+ "max": null,
1472
+ "min": 0,
1473
+ "show": true
1474
+ },
1475
+ {
1476
+ "format": "short",
1477
+ "label": null,
1478
+ "logBase": 1,
1479
+ "max": null,
1480
+ "min": null,
1481
+ "show": true
1482
+ }
1483
+ ],
1484
+ "yaxis": {
1485
+ "align": false,
1486
+ "alignLevel": null
1487
+ }
1488
+ },
1489
+ {
1490
+ "aliasColors": {
1491
+ "blocked": "#bf1b00",
1492
+ "new": "#fce2de",
1493
+ "runnable": "#7eb26d",
1494
+ "terminated": "#511749",
1495
+ "timed-waiting": "#c15c17",
1496
+ "waiting": "#eab839"
1497
+ },
1498
+ "bars": false,
1499
+ "dashLength": 10,
1500
+ "dashes": false,
1501
+ "datasource": "Prometheus",
1502
+ "fill": 1,
1503
+ "gridPos": {
1504
+ "h": 7,
1505
+ "w": 6,
1506
+ "x": 18,
1507
+ "y": 24
1508
+ },
1509
+ "id": 124,
1510
+ "legend": {
1511
+ "alignAsTable": false,
1512
+ "avg": false,
1513
+ "current": true,
1514
+ "max": true,
1515
+ "min": false,
1516
+ "rightSide": false,
1517
+ "show": true,
1518
+ "total": false,
1519
+ "values": true
1520
+ },
1521
+ "lines": true,
1522
+ "linewidth": 1,
1523
+ "links": [],
1524
+ "nullPointMode": "null",
1525
+ "paceLength": 10,
1526
+ "percentage": false,
1527
+ "pointradius": 5,
1528
+ "points": false,
1529
+ "renderer": "flot",
1530
+ "seriesOverrides": [],
1531
+ "spaceLength": 10,
1532
+ "stack": false,
1533
+ "steppedLine": false,
1534
+ "targets": [
1535
+ {
1536
+ "expr": "jvm_threads_states_threads{application=\"$application\", instance=\"$instance\"}",
1537
+ "format": "time_series",
1538
+ "intervalFactor": 2,
1539
+ "legendFormat": "{{state}}",
1540
+ "refId": "A"
1541
+ }
1542
+ ],
1543
+ "thresholds": [],
1544
+ "timeFrom": null,
1545
+ "timeRegions": [],
1546
+ "timeShift": null,
1547
+ "title": "Thread States",
1548
+ "tooltip": {
1549
+ "shared": true,
1550
+ "sort": 0,
1551
+ "value_type": "individual"
1552
+ },
1553
+ "type": "graph",
1554
+ "xaxis": {
1555
+ "buckets": null,
1556
+ "mode": "time",
1557
+ "name": null,
1558
+ "show": true,
1559
+ "values": []
1560
+ },
1561
+ "yaxes": [
1562
+ {
1563
+ "format": "short",
1564
+ "label": null,
1565
+ "logBase": 1,
1566
+ "max": null,
1567
+ "min": null,
1568
+ "show": true
1569
+ },
1570
+ {
1571
+ "format": "short",
1572
+ "label": null,
1573
+ "logBase": 1,
1574
+ "max": null,
1575
+ "min": null,
1576
+ "show": true
1577
+ }
1578
+ ],
1579
+ "yaxis": {
1580
+ "align": false,
1581
+ "alignLevel": null
1582
+ }
1583
+ },
1584
+ {
1585
+ "aliasColors": {
1586
+ "debug": "#1F78C1",
1587
+ "error": "#BF1B00",
1588
+ "info": "#508642",
1589
+ "trace": "#6ED0E0",
1590
+ "warn": "#EAB839"
1591
+ },
1592
+ "bars": false,
1593
+ "dashLength": 10,
1594
+ "dashes": false,
1595
+ "datasource": "Prometheus",
1596
+ "editable": true,
1597
+ "error": false,
1598
+ "fill": 1,
1599
+ "grid": {
1600
+ "leftLogBase": 1,
1601
+ "leftMax": null,
1602
+ "leftMin": null,
1603
+ "rightLogBase": 1,
1604
+ "rightMax": null,
1605
+ "rightMin": null
1606
+ },
1607
+ "gridPos": {
1608
+ "h": 7,
1609
+ "w": 18,
1610
+ "x": 0,
1611
+ "y": 31
1612
+ },
1613
+ "height": "",
1614
+ "id": 91,
1615
+ "legend": {
1616
+ "alignAsTable": false,
1617
+ "avg": false,
1618
+ "current": true,
1619
+ "hideEmpty": false,
1620
+ "hideZero": false,
1621
+ "max": true,
1622
+ "min": false,
1623
+ "rightSide": false,
1624
+ "show": true,
1625
+ "total": false,
1626
+ "values": true
1627
+ },
1628
+ "lines": true,
1629
+ "linewidth": 1,
1630
+ "links": [],
1631
+ "nullPointMode": "null",
1632
+ "paceLength": 10,
1633
+ "percentage": true,
1634
+ "pointradius": 5,
1635
+ "points": false,
1636
+ "renderer": "flot",
1637
+ "seriesOverrides": [
1638
+ {
1639
+ "alias": "error",
1640
+ "yaxis": 1
1641
+ },
1642
+ {
1643
+ "alias": "warn",
1644
+ "yaxis": 1
1645
+ }
1646
+ ],
1647
+ "spaceLength": 10,
1648
+ "stack": false,
1649
+ "steppedLine": false,
1650
+ "targets": [
1651
+ {
1652
+ "expr": "increase(logback_events_total{application=\"$application\", instance=\"$instance\"}[1m])",
1653
+ "format": "time_series",
1654
+ "interval": "",
1655
+ "intervalFactor": 2,
1656
+ "legendFormat": "{{level}}",
1657
+ "metric": "",
1658
+ "refId": "A",
1659
+ "step": 1200
1660
+ }
1661
+ ],
1662
+ "thresholds": [],
1663
+ "timeFrom": null,
1664
+ "timeRegions": [],
1665
+ "timeShift": null,
1666
+ "title": "Log Events (1m)",
1667
+ "tooltip": {
1668
+ "msResolution": false,
1669
+ "shared": true,
1670
+ "sort": 0,
1671
+ "value_type": "individual"
1672
+ },
1673
+ "type": "graph",
1674
+ "x-axis": true,
1675
+ "xaxis": {
1676
+ "buckets": null,
1677
+ "mode": "time",
1678
+ "name": null,
1679
+ "show": true,
1680
+ "values": []
1681
+ },
1682
+ "y-axis": true,
1683
+ "y_formats": ["short", "short"],
1684
+ "yaxes": [
1685
+ {
1686
+ "decimals": 0,
1687
+ "format": "short",
1688
+ "label": null,
1689
+ "logBase": 1,
1690
+ "max": null,
1691
+ "min": "0",
1692
+ "show": true
1693
+ },
1694
+ {
1695
+ "format": "short",
1696
+ "label": null,
1697
+ "logBase": 1,
1698
+ "max": null,
1699
+ "min": null,
1700
+ "show": true
1701
+ }
1702
+ ],
1703
+ "yaxis": {
1704
+ "align": false,
1705
+ "alignLevel": null
1706
+ }
1707
+ },
1708
+ {
1709
+ "aliasColors": {},
1710
+ "bars": false,
1711
+ "dashLength": 10,
1712
+ "dashes": false,
1713
+ "datasource": "Prometheus",
1714
+ "editable": true,
1715
+ "error": false,
1716
+ "fill": 1,
1717
+ "grid": {
1718
+ "leftLogBase": 1,
1719
+ "leftMax": null,
1720
+ "leftMin": null,
1721
+ "rightLogBase": 1,
1722
+ "rightMax": null,
1723
+ "rightMin": null
1724
+ },
1725
+ "gridPos": {
1726
+ "h": 7,
1727
+ "w": 6,
1728
+ "x": 18,
1729
+ "y": 31
1730
+ },
1731
+ "id": 61,
1732
+ "legend": {
1733
+ "avg": false,
1734
+ "current": true,
1735
+ "max": true,
1736
+ "min": false,
1737
+ "show": true,
1738
+ "total": false,
1739
+ "values": true
1740
+ },
1741
+ "lines": true,
1742
+ "linewidth": 1,
1743
+ "links": [],
1744
+ "nullPointMode": "null",
1745
+ "paceLength": 10,
1746
+ "percentage": false,
1747
+ "pointradius": 5,
1748
+ "points": false,
1749
+ "renderer": "flot",
1750
+ "seriesOverrides": [],
1751
+ "spaceLength": 10,
1752
+ "stack": false,
1753
+ "steppedLine": false,
1754
+ "targets": [
1755
+ {
1756
+ "expr": "process_open_fds{application=\"$application\", instance=\"$instance\"}",
1757
+ "format": "time_series",
1758
+ "hide": false,
1759
+ "intervalFactor": 2,
1760
+ "legendFormat": "open",
1761
+ "metric": "",
1762
+ "refId": "A",
1763
+ "step": 2400
1764
+ },
1765
+ {
1766
+ "expr": "process_max_fds{application=\"$application\", instance=\"$instance\"}",
1767
+ "format": "time_series",
1768
+ "hide": false,
1769
+ "intervalFactor": 2,
1770
+ "legendFormat": "max",
1771
+ "metric": "",
1772
+ "refId": "B",
1773
+ "step": 2400
1774
+ },
1775
+ {
1776
+ "expr": "process_files_open{application=\"$application\", instance=\"$instance\"} or process_files_open_files{application=\"$application\", instance=\"$instance\"}",
1777
+ "format": "time_series",
1778
+ "intervalFactor": 2,
1779
+ "legendFormat": "open",
1780
+ "refId": "C"
1781
+ },
1782
+ {
1783
+ "expr": "process_files_max{application=\"$application\", instance=\"$instance\"} or process_files_max_files{application=\"$application\", instance=\"$instance\"}",
1784
+ "format": "time_series",
1785
+ "intervalFactor": 2,
1786
+ "legendFormat": "max",
1787
+ "refId": "D"
1788
+ }
1789
+ ],
1790
+ "thresholds": [],
1791
+ "timeFrom": null,
1792
+ "timeRegions": [],
1793
+ "timeShift": null,
1794
+ "title": "File Descriptors",
1795
+ "tooltip": {
1796
+ "msResolution": false,
1797
+ "shared": true,
1798
+ "sort": 0,
1799
+ "value_type": "cumulative"
1800
+ },
1801
+ "type": "graph",
1802
+ "x-axis": true,
1803
+ "xaxis": {
1804
+ "buckets": null,
1805
+ "mode": "time",
1806
+ "name": null,
1807
+ "show": true,
1808
+ "values": []
1809
+ },
1810
+ "y-axis": true,
1811
+ "y_formats": ["short", "short"],
1812
+ "yaxes": [
1813
+ {
1814
+ "decimals": 0,
1815
+ "format": "short",
1816
+ "label": null,
1817
+ "logBase": 10,
1818
+ "max": null,
1819
+ "min": 0,
1820
+ "show": true
1821
+ },
1822
+ {
1823
+ "format": "short",
1824
+ "label": null,
1825
+ "logBase": 1,
1826
+ "max": null,
1827
+ "min": null,
1828
+ "show": true
1829
+ }
1830
+ ],
1831
+ "yaxis": {
1832
+ "align": false,
1833
+ "alignLevel": null
1834
+ }
1835
+ },
1836
+ {
1837
+ "collapsed": false,
1838
+ "gridPos": {
1839
+ "h": 1,
1840
+ "w": 24,
1841
+ "x": 0,
1842
+ "y": 38
1843
+ },
1844
+ "id": 129,
1845
+ "panels": [],
1846
+ "repeat": "persistence_counts",
1847
+ "title": "JVM Memory Pools (Heap)",
1848
+ "type": "row"
1849
+ },
1850
+ {
1851
+ "aliasColors": {},
1852
+ "bars": false,
1853
+ "dashLength": 10,
1854
+ "dashes": false,
1855
+ "datasource": "Prometheus",
1856
+ "editable": true,
1857
+ "error": false,
1858
+ "fill": 1,
1859
+ "grid": {
1860
+ "leftLogBase": 1,
1861
+ "leftMax": null,
1862
+ "leftMin": null,
1863
+ "rightLogBase": 1,
1864
+ "rightMax": null,
1865
+ "rightMin": null
1866
+ },
1867
+ "gridPos": {
1868
+ "h": 7,
1869
+ "w": 8,
1870
+ "x": 0,
1871
+ "y": 39
1872
+ },
1873
+ "id": 3,
1874
+ "legend": {
1875
+ "alignAsTable": false,
1876
+ "avg": false,
1877
+ "current": true,
1878
+ "max": true,
1879
+ "min": false,
1880
+ "rightSide": false,
1881
+ "show": true,
1882
+ "total": false,
1883
+ "values": true
1884
+ },
1885
+ "lines": true,
1886
+ "linewidth": 1,
1887
+ "links": [],
1888
+ "maxPerRow": 3,
1889
+ "nullPointMode": "null",
1890
+ "paceLength": 10,
1891
+ "percentage": false,
1892
+ "pointradius": 5,
1893
+ "points": false,
1894
+ "renderer": "flot",
1895
+ "repeat": "jvm_memory_pool_heap",
1896
+ "scopedVars": {
1897
+ "jvm_memory_pool_heap": {
1898
+ "selected": false,
1899
+ "text": "PS Eden Space",
1900
+ "value": "PS Eden Space"
1901
+ }
1902
+ },
1903
+ "seriesOverrides": [],
1904
+ "spaceLength": 10,
1905
+ "stack": false,
1906
+ "steppedLine": false,
1907
+ "targets": [
1908
+ {
1909
+ "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
1910
+ "format": "time_series",
1911
+ "hide": false,
1912
+ "interval": "",
1913
+ "intervalFactor": 2,
1914
+ "legendFormat": "used",
1915
+ "metric": "",
1916
+ "refId": "A",
1917
+ "step": 1800
1918
+ },
1919
+ {
1920
+ "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
1921
+ "format": "time_series",
1922
+ "hide": false,
1923
+ "interval": "",
1924
+ "intervalFactor": 2,
1925
+ "legendFormat": "commited",
1926
+ "metric": "",
1927
+ "refId": "B",
1928
+ "step": 1800
1929
+ },
1930
+ {
1931
+ "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
1932
+ "format": "time_series",
1933
+ "hide": false,
1934
+ "interval": "",
1935
+ "intervalFactor": 2,
1936
+ "legendFormat": "max",
1937
+ "metric": "",
1938
+ "refId": "C",
1939
+ "step": 1800
1940
+ }
1941
+ ],
1942
+ "thresholds": [],
1943
+ "timeFrom": null,
1944
+ "timeRegions": [],
1945
+ "timeShift": null,
1946
+ "title": "$jvm_memory_pool_heap",
1947
+ "tooltip": {
1948
+ "msResolution": false,
1949
+ "shared": true,
1950
+ "sort": 0,
1951
+ "value_type": "cumulative"
1952
+ },
1953
+ "type": "graph",
1954
+ "x-axis": true,
1955
+ "xaxis": {
1956
+ "buckets": null,
1957
+ "mode": "time",
1958
+ "name": null,
1959
+ "show": true,
1960
+ "values": []
1961
+ },
1962
+ "y-axis": true,
1963
+ "y_formats": ["mbytes", "short"],
1964
+ "yaxes": [
1965
+ {
1966
+ "format": "bytes",
1967
+ "label": null,
1968
+ "logBase": 1,
1969
+ "max": null,
1970
+ "min": 0,
1971
+ "show": true
1972
+ },
1973
+ {
1974
+ "format": "short",
1975
+ "label": null,
1976
+ "logBase": 1,
1977
+ "max": null,
1978
+ "min": null,
1979
+ "show": true
1980
+ }
1981
+ ],
1982
+ "yaxis": {
1983
+ "align": false,
1984
+ "alignLevel": null
1985
+ }
1986
+ },
1987
+ {
1988
+ "aliasColors": {},
1989
+ "bars": false,
1990
+ "dashLength": 10,
1991
+ "dashes": false,
1992
+ "datasource": "Prometheus",
1993
+ "editable": true,
1994
+ "error": false,
1995
+ "fill": 1,
1996
+ "grid": {
1997
+ "leftLogBase": 1,
1998
+ "leftMax": null,
1999
+ "leftMin": null,
2000
+ "rightLogBase": 1,
2001
+ "rightMax": null,
2002
+ "rightMin": null
2003
+ },
2004
+ "gridPos": {
2005
+ "h": 7,
2006
+ "w": 8,
2007
+ "x": 8,
2008
+ "y": 39
2009
+ },
2010
+ "id": 134,
2011
+ "legend": {
2012
+ "alignAsTable": false,
2013
+ "avg": false,
2014
+ "current": true,
2015
+ "max": true,
2016
+ "min": false,
2017
+ "rightSide": false,
2018
+ "show": true,
2019
+ "total": false,
2020
+ "values": true
2021
+ },
2022
+ "lines": true,
2023
+ "linewidth": 1,
2024
+ "links": [],
2025
+ "maxPerRow": 3,
2026
+ "nullPointMode": "null",
2027
+ "paceLength": 10,
2028
+ "percentage": false,
2029
+ "pointradius": 5,
2030
+ "points": false,
2031
+ "renderer": "flot",
2032
+ "repeat": null,
2033
+ "repeatIteration": 1553765841423,
2034
+ "repeatPanelId": 3,
2035
+ "scopedVars": {
2036
+ "jvm_memory_pool_heap": {
2037
+ "selected": false,
2038
+ "text": "PS Old Gen",
2039
+ "value": "PS Old Gen"
2040
+ }
2041
+ },
2042
+ "seriesOverrides": [],
2043
+ "spaceLength": 10,
2044
+ "stack": false,
2045
+ "steppedLine": false,
2046
+ "targets": [
2047
+ {
2048
+ "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
2049
+ "format": "time_series",
2050
+ "hide": false,
2051
+ "interval": "",
2052
+ "intervalFactor": 2,
2053
+ "legendFormat": "used",
2054
+ "metric": "",
2055
+ "refId": "A",
2056
+ "step": 1800
2057
+ },
2058
+ {
2059
+ "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
2060
+ "format": "time_series",
2061
+ "hide": false,
2062
+ "interval": "",
2063
+ "intervalFactor": 2,
2064
+ "legendFormat": "commited",
2065
+ "metric": "",
2066
+ "refId": "B",
2067
+ "step": 1800
2068
+ },
2069
+ {
2070
+ "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
2071
+ "format": "time_series",
2072
+ "hide": false,
2073
+ "interval": "",
2074
+ "intervalFactor": 2,
2075
+ "legendFormat": "max",
2076
+ "metric": "",
2077
+ "refId": "C",
2078
+ "step": 1800
2079
+ }
2080
+ ],
2081
+ "thresholds": [],
2082
+ "timeFrom": null,
2083
+ "timeRegions": [],
2084
+ "timeShift": null,
2085
+ "title": "$jvm_memory_pool_heap",
2086
+ "tooltip": {
2087
+ "msResolution": false,
2088
+ "shared": true,
2089
+ "sort": 0,
2090
+ "value_type": "cumulative"
2091
+ },
2092
+ "type": "graph",
2093
+ "x-axis": true,
2094
+ "xaxis": {
2095
+ "buckets": null,
2096
+ "mode": "time",
2097
+ "name": null,
2098
+ "show": true,
2099
+ "values": []
2100
+ },
2101
+ "y-axis": true,
2102
+ "y_formats": ["mbytes", "short"],
2103
+ "yaxes": [
2104
+ {
2105
+ "format": "bytes",
2106
+ "label": null,
2107
+ "logBase": 1,
2108
+ "max": null,
2109
+ "min": 0,
2110
+ "show": true
2111
+ },
2112
+ {
2113
+ "format": "short",
2114
+ "label": null,
2115
+ "logBase": 1,
2116
+ "max": null,
2117
+ "min": null,
2118
+ "show": true
2119
+ }
2120
+ ],
2121
+ "yaxis": {
2122
+ "align": false,
2123
+ "alignLevel": null
2124
+ }
2125
+ },
2126
+ {
2127
+ "aliasColors": {},
2128
+ "bars": false,
2129
+ "dashLength": 10,
2130
+ "dashes": false,
2131
+ "datasource": "Prometheus",
2132
+ "editable": true,
2133
+ "error": false,
2134
+ "fill": 1,
2135
+ "grid": {
2136
+ "leftLogBase": 1,
2137
+ "leftMax": null,
2138
+ "leftMin": null,
2139
+ "rightLogBase": 1,
2140
+ "rightMax": null,
2141
+ "rightMin": null
2142
+ },
2143
+ "gridPos": {
2144
+ "h": 7,
2145
+ "w": 8,
2146
+ "x": 16,
2147
+ "y": 39
2148
+ },
2149
+ "id": 135,
2150
+ "legend": {
2151
+ "alignAsTable": false,
2152
+ "avg": false,
2153
+ "current": true,
2154
+ "max": true,
2155
+ "min": false,
2156
+ "rightSide": false,
2157
+ "show": true,
2158
+ "total": false,
2159
+ "values": true
2160
+ },
2161
+ "lines": true,
2162
+ "linewidth": 1,
2163
+ "links": [],
2164
+ "maxPerRow": 3,
2165
+ "nullPointMode": "null",
2166
+ "paceLength": 10,
2167
+ "percentage": false,
2168
+ "pointradius": 5,
2169
+ "points": false,
2170
+ "renderer": "flot",
2171
+ "repeat": null,
2172
+ "repeatIteration": 1553765841423,
2173
+ "repeatPanelId": 3,
2174
+ "scopedVars": {
2175
+ "jvm_memory_pool_heap": {
2176
+ "selected": false,
2177
+ "text": "PS Survivor Space",
2178
+ "value": "PS Survivor Space"
2179
+ }
2180
+ },
2181
+ "seriesOverrides": [],
2182
+ "spaceLength": 10,
2183
+ "stack": false,
2184
+ "steppedLine": false,
2185
+ "targets": [
2186
+ {
2187
+ "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
2188
+ "format": "time_series",
2189
+ "hide": false,
2190
+ "interval": "",
2191
+ "intervalFactor": 2,
2192
+ "legendFormat": "used",
2193
+ "metric": "",
2194
+ "refId": "A",
2195
+ "step": 1800
2196
+ },
2197
+ {
2198
+ "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
2199
+ "format": "time_series",
2200
+ "hide": false,
2201
+ "interval": "",
2202
+ "intervalFactor": 2,
2203
+ "legendFormat": "commited",
2204
+ "metric": "",
2205
+ "refId": "B",
2206
+ "step": 1800
2207
+ },
2208
+ {
2209
+ "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}",
2210
+ "format": "time_series",
2211
+ "hide": false,
2212
+ "interval": "",
2213
+ "intervalFactor": 2,
2214
+ "legendFormat": "max",
2215
+ "metric": "",
2216
+ "refId": "C",
2217
+ "step": 1800
2218
+ }
2219
+ ],
2220
+ "thresholds": [],
2221
+ "timeFrom": null,
2222
+ "timeRegions": [],
2223
+ "timeShift": null,
2224
+ "title": "$jvm_memory_pool_heap",
2225
+ "tooltip": {
2226
+ "msResolution": false,
2227
+ "shared": true,
2228
+ "sort": 0,
2229
+ "value_type": "cumulative"
2230
+ },
2231
+ "type": "graph",
2232
+ "x-axis": true,
2233
+ "xaxis": {
2234
+ "buckets": null,
2235
+ "mode": "time",
2236
+ "name": null,
2237
+ "show": true,
2238
+ "values": []
2239
+ },
2240
+ "y-axis": true,
2241
+ "y_formats": ["mbytes", "short"],
2242
+ "yaxes": [
2243
+ {
2244
+ "format": "bytes",
2245
+ "label": null,
2246
+ "logBase": 1,
2247
+ "max": null,
2248
+ "min": 0,
2249
+ "show": true
2250
+ },
2251
+ {
2252
+ "format": "short",
2253
+ "label": null,
2254
+ "logBase": 1,
2255
+ "max": null,
2256
+ "min": null,
2257
+ "show": true
2258
+ }
2259
+ ],
2260
+ "yaxis": {
2261
+ "align": false,
2262
+ "alignLevel": null
2263
+ }
2264
+ },
2265
+ {
2266
+ "collapsed": false,
2267
+ "gridPos": {
2268
+ "h": 1,
2269
+ "w": 24,
2270
+ "x": 0,
2271
+ "y": 46
2272
+ },
2273
+ "id": 130,
2274
+ "panels": [],
2275
+ "repeat": null,
2276
+ "title": "JVM Memory Pools (Non-Heap)",
2277
+ "type": "row"
2278
+ },
2279
+ {
2280
+ "aliasColors": {},
2281
+ "bars": false,
2282
+ "dashLength": 10,
2283
+ "dashes": false,
2284
+ "datasource": "Prometheus",
2285
+ "editable": true,
2286
+ "error": false,
2287
+ "fill": 1,
2288
+ "grid": {
2289
+ "leftLogBase": 1,
2290
+ "leftMax": null,
2291
+ "leftMin": null,
2292
+ "rightLogBase": 1,
2293
+ "rightMax": null,
2294
+ "rightMin": null
2295
+ },
2296
+ "gridPos": {
2297
+ "h": 7,
2298
+ "w": 8,
2299
+ "x": 0,
2300
+ "y": 47
2301
+ },
2302
+ "id": 78,
2303
+ "legend": {
2304
+ "alignAsTable": false,
2305
+ "avg": false,
2306
+ "current": true,
2307
+ "max": true,
2308
+ "min": false,
2309
+ "rightSide": false,
2310
+ "show": true,
2311
+ "total": false,
2312
+ "values": true
2313
+ },
2314
+ "lines": true,
2315
+ "linewidth": 1,
2316
+ "links": [],
2317
+ "maxPerRow": 3,
2318
+ "nullPointMode": "null",
2319
+ "paceLength": 10,
2320
+ "percentage": false,
2321
+ "pointradius": 5,
2322
+ "points": false,
2323
+ "renderer": "flot",
2324
+ "repeat": "jvm_memory_pool_nonheap",
2325
+ "scopedVars": {
2326
+ "jvm_memory_pool_nonheap": {
2327
+ "selected": false,
2328
+ "text": "Metaspace",
2329
+ "value": "Metaspace"
2330
+ }
2331
+ },
2332
+ "seriesOverrides": [],
2333
+ "spaceLength": 10,
2334
+ "stack": false,
2335
+ "steppedLine": false,
2336
+ "targets": [
2337
+ {
2338
+ "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2339
+ "format": "time_series",
2340
+ "hide": false,
2341
+ "interval": "",
2342
+ "intervalFactor": 2,
2343
+ "legendFormat": "used",
2344
+ "metric": "",
2345
+ "refId": "A",
2346
+ "step": 1800
2347
+ },
2348
+ {
2349
+ "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2350
+ "format": "time_series",
2351
+ "hide": false,
2352
+ "interval": "",
2353
+ "intervalFactor": 2,
2354
+ "legendFormat": "commited",
2355
+ "metric": "",
2356
+ "refId": "B",
2357
+ "step": 1800
2358
+ },
2359
+ {
2360
+ "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2361
+ "format": "time_series",
2362
+ "hide": false,
2363
+ "interval": "",
2364
+ "intervalFactor": 2,
2365
+ "legendFormat": "max",
2366
+ "metric": "",
2367
+ "refId": "C",
2368
+ "step": 1800
2369
+ }
2370
+ ],
2371
+ "thresholds": [],
2372
+ "timeFrom": null,
2373
+ "timeRegions": [],
2374
+ "timeShift": null,
2375
+ "title": "$jvm_memory_pool_nonheap",
2376
+ "tooltip": {
2377
+ "msResolution": false,
2378
+ "shared": true,
2379
+ "sort": 0,
2380
+ "value_type": "cumulative"
2381
+ },
2382
+ "type": "graph",
2383
+ "x-axis": true,
2384
+ "xaxis": {
2385
+ "buckets": null,
2386
+ "mode": "time",
2387
+ "name": null,
2388
+ "show": true,
2389
+ "values": []
2390
+ },
2391
+ "y-axis": true,
2392
+ "y_formats": ["mbytes", "short"],
2393
+ "yaxes": [
2394
+ {
2395
+ "format": "bytes",
2396
+ "label": null,
2397
+ "logBase": 1,
2398
+ "max": null,
2399
+ "min": 0,
2400
+ "show": true
2401
+ },
2402
+ {
2403
+ "format": "short",
2404
+ "label": null,
2405
+ "logBase": 1,
2406
+ "max": null,
2407
+ "min": null,
2408
+ "show": true
2409
+ }
2410
+ ],
2411
+ "yaxis": {
2412
+ "align": false,
2413
+ "alignLevel": null
2414
+ }
2415
+ },
2416
+ {
2417
+ "aliasColors": {},
2418
+ "bars": false,
2419
+ "dashLength": 10,
2420
+ "dashes": false,
2421
+ "datasource": "Prometheus",
2422
+ "editable": true,
2423
+ "error": false,
2424
+ "fill": 1,
2425
+ "grid": {
2426
+ "leftLogBase": 1,
2427
+ "leftMax": null,
2428
+ "leftMin": null,
2429
+ "rightLogBase": 1,
2430
+ "rightMax": null,
2431
+ "rightMin": null
2432
+ },
2433
+ "gridPos": {
2434
+ "h": 7,
2435
+ "w": 8,
2436
+ "x": 8,
2437
+ "y": 47
2438
+ },
2439
+ "id": 136,
2440
+ "legend": {
2441
+ "alignAsTable": false,
2442
+ "avg": false,
2443
+ "current": true,
2444
+ "max": true,
2445
+ "min": false,
2446
+ "rightSide": false,
2447
+ "show": true,
2448
+ "total": false,
2449
+ "values": true
2450
+ },
2451
+ "lines": true,
2452
+ "linewidth": 1,
2453
+ "links": [],
2454
+ "maxPerRow": 3,
2455
+ "nullPointMode": "null",
2456
+ "paceLength": 10,
2457
+ "percentage": false,
2458
+ "pointradius": 5,
2459
+ "points": false,
2460
+ "renderer": "flot",
2461
+ "repeat": null,
2462
+ "repeatIteration": 1553765841423,
2463
+ "repeatPanelId": 78,
2464
+ "scopedVars": {
2465
+ "jvm_memory_pool_nonheap": {
2466
+ "selected": false,
2467
+ "text": "Compressed Class Space",
2468
+ "value": "Compressed Class Space"
2469
+ }
2470
+ },
2471
+ "seriesOverrides": [],
2472
+ "spaceLength": 10,
2473
+ "stack": false,
2474
+ "steppedLine": false,
2475
+ "targets": [
2476
+ {
2477
+ "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2478
+ "format": "time_series",
2479
+ "hide": false,
2480
+ "interval": "",
2481
+ "intervalFactor": 2,
2482
+ "legendFormat": "used",
2483
+ "metric": "",
2484
+ "refId": "A",
2485
+ "step": 1800
2486
+ },
2487
+ {
2488
+ "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2489
+ "format": "time_series",
2490
+ "hide": false,
2491
+ "interval": "",
2492
+ "intervalFactor": 2,
2493
+ "legendFormat": "commited",
2494
+ "metric": "",
2495
+ "refId": "B",
2496
+ "step": 1800
2497
+ },
2498
+ {
2499
+ "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2500
+ "format": "time_series",
2501
+ "hide": false,
2502
+ "interval": "",
2503
+ "intervalFactor": 2,
2504
+ "legendFormat": "max",
2505
+ "metric": "",
2506
+ "refId": "C",
2507
+ "step": 1800
2508
+ }
2509
+ ],
2510
+ "thresholds": [],
2511
+ "timeFrom": null,
2512
+ "timeRegions": [],
2513
+ "timeShift": null,
2514
+ "title": "$jvm_memory_pool_nonheap",
2515
+ "tooltip": {
2516
+ "msResolution": false,
2517
+ "shared": true,
2518
+ "sort": 0,
2519
+ "value_type": "cumulative"
2520
+ },
2521
+ "type": "graph",
2522
+ "x-axis": true,
2523
+ "xaxis": {
2524
+ "buckets": null,
2525
+ "mode": "time",
2526
+ "name": null,
2527
+ "show": true,
2528
+ "values": []
2529
+ },
2530
+ "y-axis": true,
2531
+ "y_formats": ["mbytes", "short"],
2532
+ "yaxes": [
2533
+ {
2534
+ "format": "bytes",
2535
+ "label": null,
2536
+ "logBase": 1,
2537
+ "max": null,
2538
+ "min": 0,
2539
+ "show": true
2540
+ },
2541
+ {
2542
+ "format": "short",
2543
+ "label": null,
2544
+ "logBase": 1,
2545
+ "max": null,
2546
+ "min": null,
2547
+ "show": true
2548
+ }
2549
+ ],
2550
+ "yaxis": {
2551
+ "align": false,
2552
+ "alignLevel": null
2553
+ }
2554
+ },
2555
+ {
2556
+ "aliasColors": {},
2557
+ "bars": false,
2558
+ "dashLength": 10,
2559
+ "dashes": false,
2560
+ "datasource": "Prometheus",
2561
+ "editable": true,
2562
+ "error": false,
2563
+ "fill": 1,
2564
+ "grid": {
2565
+ "leftLogBase": 1,
2566
+ "leftMax": null,
2567
+ "leftMin": null,
2568
+ "rightLogBase": 1,
2569
+ "rightMax": null,
2570
+ "rightMin": null
2571
+ },
2572
+ "gridPos": {
2573
+ "h": 7,
2574
+ "w": 8,
2575
+ "x": 16,
2576
+ "y": 47
2577
+ },
2578
+ "id": 137,
2579
+ "legend": {
2580
+ "alignAsTable": false,
2581
+ "avg": false,
2582
+ "current": true,
2583
+ "max": true,
2584
+ "min": false,
2585
+ "rightSide": false,
2586
+ "show": true,
2587
+ "total": false,
2588
+ "values": true
2589
+ },
2590
+ "lines": true,
2591
+ "linewidth": 1,
2592
+ "links": [],
2593
+ "maxPerRow": 3,
2594
+ "nullPointMode": "null",
2595
+ "paceLength": 10,
2596
+ "percentage": false,
2597
+ "pointradius": 5,
2598
+ "points": false,
2599
+ "renderer": "flot",
2600
+ "repeat": null,
2601
+ "repeatIteration": 1553765841423,
2602
+ "repeatPanelId": 78,
2603
+ "scopedVars": {
2604
+ "jvm_memory_pool_nonheap": {
2605
+ "selected": false,
2606
+ "text": "Code Cache",
2607
+ "value": "Code Cache"
2608
+ }
2609
+ },
2610
+ "seriesOverrides": [],
2611
+ "spaceLength": 10,
2612
+ "stack": false,
2613
+ "steppedLine": false,
2614
+ "targets": [
2615
+ {
2616
+ "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2617
+ "format": "time_series",
2618
+ "hide": false,
2619
+ "interval": "",
2620
+ "intervalFactor": 2,
2621
+ "legendFormat": "used",
2622
+ "metric": "",
2623
+ "refId": "A",
2624
+ "step": 1800
2625
+ },
2626
+ {
2627
+ "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2628
+ "format": "time_series",
2629
+ "hide": false,
2630
+ "interval": "",
2631
+ "intervalFactor": 2,
2632
+ "legendFormat": "commited",
2633
+ "metric": "",
2634
+ "refId": "B",
2635
+ "step": 1800
2636
+ },
2637
+ {
2638
+ "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}",
2639
+ "format": "time_series",
2640
+ "hide": false,
2641
+ "interval": "",
2642
+ "intervalFactor": 2,
2643
+ "legendFormat": "max",
2644
+ "metric": "",
2645
+ "refId": "C",
2646
+ "step": 1800
2647
+ }
2648
+ ],
2649
+ "thresholds": [],
2650
+ "timeFrom": null,
2651
+ "timeRegions": [],
2652
+ "timeShift": null,
2653
+ "title": "$jvm_memory_pool_nonheap",
2654
+ "tooltip": {
2655
+ "msResolution": false,
2656
+ "shared": true,
2657
+ "sort": 0,
2658
+ "value_type": "cumulative"
2659
+ },
2660
+ "type": "graph",
2661
+ "x-axis": true,
2662
+ "xaxis": {
2663
+ "buckets": null,
2664
+ "mode": "time",
2665
+ "name": null,
2666
+ "show": true,
2667
+ "values": []
2668
+ },
2669
+ "y-axis": true,
2670
+ "y_formats": ["mbytes", "short"],
2671
+ "yaxes": [
2672
+ {
2673
+ "format": "bytes",
2674
+ "label": null,
2675
+ "logBase": 1,
2676
+ "max": null,
2677
+ "min": 0,
2678
+ "show": true
2679
+ },
2680
+ {
2681
+ "format": "short",
2682
+ "label": null,
2683
+ "logBase": 1,
2684
+ "max": null,
2685
+ "min": null,
2686
+ "show": true
2687
+ }
2688
+ ],
2689
+ "yaxis": {
2690
+ "align": false,
2691
+ "alignLevel": null
2692
+ }
2693
+ },
2694
+ {
2695
+ "collapsed": false,
2696
+ "gridPos": {
2697
+ "h": 1,
2698
+ "w": 24,
2699
+ "x": 0,
2700
+ "y": 54
2701
+ },
2702
+ "id": 131,
2703
+ "panels": [],
2704
+ "repeat": null,
2705
+ "title": "Garbage Collection",
2706
+ "type": "row"
2707
+ },
2708
+ {
2709
+ "aliasColors": {},
2710
+ "bars": false,
2711
+ "dashLength": 10,
2712
+ "dashes": false,
2713
+ "datasource": "Prometheus",
2714
+ "fill": 1,
2715
+ "gridPos": {
2716
+ "h": 7,
2717
+ "w": 8,
2718
+ "x": 0,
2719
+ "y": 55
2720
+ },
2721
+ "id": 98,
2722
+ "legend": {
2723
+ "avg": false,
2724
+ "current": false,
2725
+ "max": false,
2726
+ "min": false,
2727
+ "show": true,
2728
+ "total": false,
2729
+ "values": false
2730
+ },
2731
+ "lines": true,
2732
+ "linewidth": 1,
2733
+ "links": [],
2734
+ "nullPointMode": "null",
2735
+ "paceLength": 10,
2736
+ "percentage": false,
2737
+ "pointradius": 5,
2738
+ "points": false,
2739
+ "renderer": "flot",
2740
+ "seriesOverrides": [],
2741
+ "spaceLength": 10,
2742
+ "stack": false,
2743
+ "steppedLine": false,
2744
+ "targets": [
2745
+ {
2746
+ "expr": "rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])",
2747
+ "format": "time_series",
2748
+ "hide": false,
2749
+ "intervalFactor": 2,
2750
+ "legendFormat": "{{action}} ({{cause}})",
2751
+ "refId": "A"
2752
+ }
2753
+ ],
2754
+ "thresholds": [],
2755
+ "timeFrom": null,
2756
+ "timeRegions": [],
2757
+ "timeShift": null,
2758
+ "title": "Collections",
2759
+ "tooltip": {
2760
+ "shared": true,
2761
+ "sort": 0,
2762
+ "value_type": "individual"
2763
+ },
2764
+ "type": "graph",
2765
+ "xaxis": {
2766
+ "buckets": null,
2767
+ "mode": "time",
2768
+ "name": null,
2769
+ "show": true,
2770
+ "values": []
2771
+ },
2772
+ "yaxes": [
2773
+ {
2774
+ "format": "ops",
2775
+ "label": null,
2776
+ "logBase": 1,
2777
+ "max": null,
2778
+ "min": "0",
2779
+ "show": true
2780
+ },
2781
+ {
2782
+ "format": "short",
2783
+ "label": "",
2784
+ "logBase": 1,
2785
+ "max": null,
2786
+ "min": null,
2787
+ "show": true
2788
+ }
2789
+ ],
2790
+ "yaxis": {
2791
+ "align": false,
2792
+ "alignLevel": null
2793
+ }
2794
+ },
2795
+ {
2796
+ "aliasColors": {},
2797
+ "bars": false,
2798
+ "dashLength": 10,
2799
+ "dashes": false,
2800
+ "datasource": "Prometheus",
2801
+ "fill": 1,
2802
+ "gridPos": {
2803
+ "h": 7,
2804
+ "w": 8,
2805
+ "x": 8,
2806
+ "y": 55
2807
+ },
2808
+ "id": 101,
2809
+ "legend": {
2810
+ "avg": false,
2811
+ "current": false,
2812
+ "max": false,
2813
+ "min": false,
2814
+ "show": true,
2815
+ "total": false,
2816
+ "values": false
2817
+ },
2818
+ "lines": true,
2819
+ "linewidth": 1,
2820
+ "links": [],
2821
+ "nullPointMode": "null",
2822
+ "paceLength": 10,
2823
+ "percentage": false,
2824
+ "pointradius": 5,
2825
+ "points": false,
2826
+ "renderer": "flot",
2827
+ "seriesOverrides": [],
2828
+ "spaceLength": 10,
2829
+ "stack": false,
2830
+ "steppedLine": false,
2831
+ "targets": [
2832
+ {
2833
+ "expr": "rate(jvm_gc_pause_seconds_sum{application=\"$application\", instance=\"$instance\"}[1m])/rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])",
2834
+ "format": "time_series",
2835
+ "hide": false,
2836
+ "instant": false,
2837
+ "intervalFactor": 1,
2838
+ "legendFormat": "avg {{action}} ({{cause}})",
2839
+ "refId": "A"
2840
+ },
2841
+ {
2842
+ "expr": "jvm_gc_pause_seconds_max{application=\"$application\", instance=\"$instance\"}",
2843
+ "format": "time_series",
2844
+ "hide": false,
2845
+ "instant": false,
2846
+ "intervalFactor": 1,
2847
+ "legendFormat": "max {{action}} ({{cause}})",
2848
+ "refId": "B"
2849
+ }
2850
+ ],
2851
+ "thresholds": [],
2852
+ "timeFrom": null,
2853
+ "timeRegions": [],
2854
+ "timeShift": null,
2855
+ "title": "Pause Durations",
2856
+ "tooltip": {
2857
+ "shared": true,
2858
+ "sort": 0,
2859
+ "value_type": "individual"
2860
+ },
2861
+ "type": "graph",
2862
+ "xaxis": {
2863
+ "buckets": null,
2864
+ "mode": "time",
2865
+ "name": null,
2866
+ "show": true,
2867
+ "values": []
2868
+ },
2869
+ "yaxes": [
2870
+ {
2871
+ "format": "s",
2872
+ "label": null,
2873
+ "logBase": 1,
2874
+ "max": null,
2875
+ "min": "0",
2876
+ "show": true
2877
+ },
2878
+ {
2879
+ "format": "short",
2880
+ "label": "",
2881
+ "logBase": 1,
2882
+ "max": null,
2883
+ "min": null,
2884
+ "show": true
2885
+ }
2886
+ ],
2887
+ "yaxis": {
2888
+ "align": false,
2889
+ "alignLevel": null
2890
+ }
2891
+ },
2892
+ {
2893
+ "aliasColors": {},
2894
+ "bars": false,
2895
+ "dashLength": 10,
2896
+ "dashes": false,
2897
+ "datasource": "Prometheus",
2898
+ "fill": 1,
2899
+ "gridPos": {
2900
+ "h": 7,
2901
+ "w": 8,
2902
+ "x": 16,
2903
+ "y": 55
2904
+ },
2905
+ "id": 99,
2906
+ "legend": {
2907
+ "avg": false,
2908
+ "current": false,
2909
+ "max": false,
2910
+ "min": false,
2911
+ "show": true,
2912
+ "total": false,
2913
+ "values": false
2914
+ },
2915
+ "lines": true,
2916
+ "linewidth": 1,
2917
+ "links": [],
2918
+ "nullPointMode": "null",
2919
+ "paceLength": 10,
2920
+ "percentage": false,
2921
+ "pointradius": 5,
2922
+ "points": false,
2923
+ "renderer": "flot",
2924
+ "seriesOverrides": [],
2925
+ "spaceLength": 10,
2926
+ "stack": false,
2927
+ "steppedLine": false,
2928
+ "targets": [
2929
+ {
2930
+ "expr": "rate(jvm_gc_memory_allocated_bytes_total{application=\"$application\", instance=\"$instance\"}[1m])",
2931
+ "format": "time_series",
2932
+ "interval": "",
2933
+ "intervalFactor": 1,
2934
+ "legendFormat": "allocated",
2935
+ "refId": "A"
2936
+ },
2937
+ {
2938
+ "expr": "rate(jvm_gc_memory_promoted_bytes_total{application=\"$application\", instance=\"$instance\"}[1m])",
2939
+ "format": "time_series",
2940
+ "interval": "",
2941
+ "intervalFactor": 1,
2942
+ "legendFormat": "promoted",
2943
+ "refId": "B"
2944
+ }
2945
+ ],
2946
+ "thresholds": [],
2947
+ "timeFrom": null,
2948
+ "timeRegions": [],
2949
+ "timeShift": null,
2950
+ "title": "Allocated/Promoted",
2951
+ "tooltip": {
2952
+ "shared": true,
2953
+ "sort": 0,
2954
+ "value_type": "individual"
2955
+ },
2956
+ "type": "graph",
2957
+ "xaxis": {
2958
+ "buckets": null,
2959
+ "mode": "time",
2960
+ "name": null,
2961
+ "show": true,
2962
+ "values": []
2963
+ },
2964
+ "yaxes": [
2965
+ {
2966
+ "format": "bytes",
2967
+ "label": null,
2968
+ "logBase": 1,
2969
+ "max": null,
2970
+ "min": "0",
2971
+ "show": true
2972
+ },
2973
+ {
2974
+ "format": "short",
2975
+ "label": null,
2976
+ "logBase": 1,
2977
+ "max": null,
2978
+ "min": null,
2979
+ "show": true
2980
+ }
2981
+ ],
2982
+ "yaxis": {
2983
+ "align": false,
2984
+ "alignLevel": null
2985
+ }
2986
+ },
2987
+ {
2988
+ "collapsed": false,
2989
+ "gridPos": {
2990
+ "h": 1,
2991
+ "w": 24,
2992
+ "x": 0,
2993
+ "y": 62
2994
+ },
2995
+ "id": 132,
2996
+ "panels": [],
2997
+ "repeat": null,
2998
+ "title": "Classloading",
2999
+ "type": "row"
3000
+ },
3001
+ {
3002
+ "aliasColors": {},
3003
+ "bars": false,
3004
+ "dashLength": 10,
3005
+ "dashes": false,
3006
+ "datasource": "Prometheus",
3007
+ "editable": true,
3008
+ "error": false,
3009
+ "fill": 1,
3010
+ "grid": {
3011
+ "leftLogBase": 1,
3012
+ "leftMax": null,
3013
+ "leftMin": null,
3014
+ "rightLogBase": 1,
3015
+ "rightMax": null,
3016
+ "rightMin": null
3017
+ },
3018
+ "gridPos": {
3019
+ "h": 7,
3020
+ "w": 12,
3021
+ "x": 0,
3022
+ "y": 63
3023
+ },
3024
+ "id": 37,
3025
+ "legend": {
3026
+ "avg": false,
3027
+ "current": false,
3028
+ "max": false,
3029
+ "min": false,
3030
+ "show": true,
3031
+ "total": false,
3032
+ "values": false
3033
+ },
3034
+ "lines": true,
3035
+ "linewidth": 1,
3036
+ "links": [],
3037
+ "nullPointMode": "null",
3038
+ "paceLength": 10,
3039
+ "percentage": false,
3040
+ "pointradius": 5,
3041
+ "points": false,
3042
+ "renderer": "flot",
3043
+ "seriesOverrides": [],
3044
+ "spaceLength": 10,
3045
+ "stack": false,
3046
+ "steppedLine": false,
3047
+ "targets": [
3048
+ {
3049
+ "expr": "jvm_classes_loaded{application=\"$application\", instance=\"$instance\"} or jvm_classes_loaded_classes{application=\"$application\", instance=\"$instance\"}",
3050
+ "format": "time_series",
3051
+ "intervalFactor": 2,
3052
+ "legendFormat": "loaded",
3053
+ "metric": "",
3054
+ "refId": "A",
3055
+ "step": 1200
3056
+ }
3057
+ ],
3058
+ "thresholds": [],
3059
+ "timeFrom": null,
3060
+ "timeRegions": [],
3061
+ "timeShift": null,
3062
+ "title": "Classes loaded",
3063
+ "tooltip": {
3064
+ "msResolution": false,
3065
+ "shared": true,
3066
+ "sort": 0,
3067
+ "value_type": "cumulative"
3068
+ },
3069
+ "type": "graph",
3070
+ "x-axis": true,
3071
+ "xaxis": {
3072
+ "buckets": null,
3073
+ "mode": "time",
3074
+ "name": null,
3075
+ "show": true,
3076
+ "values": []
3077
+ },
3078
+ "y-axis": true,
3079
+ "y_formats": ["short", "short"],
3080
+ "yaxes": [
3081
+ {
3082
+ "format": "short",
3083
+ "label": null,
3084
+ "logBase": 1,
3085
+ "max": null,
3086
+ "min": 0,
3087
+ "show": true
3088
+ },
3089
+ {
3090
+ "format": "short",
3091
+ "label": null,
3092
+ "logBase": 1,
3093
+ "max": null,
3094
+ "min": null,
3095
+ "show": true
3096
+ }
3097
+ ],
3098
+ "yaxis": {
3099
+ "align": false,
3100
+ "alignLevel": null
3101
+ }
3102
+ },
3103
+ {
3104
+ "aliasColors": {},
3105
+ "bars": false,
3106
+ "dashLength": 10,
3107
+ "dashes": false,
3108
+ "datasource": "Prometheus",
3109
+ "editable": true,
3110
+ "error": false,
3111
+ "fill": 1,
3112
+ "grid": {
3113
+ "leftLogBase": 1,
3114
+ "leftMax": null,
3115
+ "leftMin": null,
3116
+ "rightLogBase": 1,
3117
+ "rightMax": null,
3118
+ "rightMin": null
3119
+ },
3120
+ "gridPos": {
3121
+ "h": 7,
3122
+ "w": 12,
3123
+ "x": 12,
3124
+ "y": 63
3125
+ },
3126
+ "id": 38,
3127
+ "legend": {
3128
+ "avg": false,
3129
+ "current": false,
3130
+ "max": false,
3131
+ "min": false,
3132
+ "show": true,
3133
+ "total": false,
3134
+ "values": false
3135
+ },
3136
+ "lines": true,
3137
+ "linewidth": 1,
3138
+ "links": [],
3139
+ "nullPointMode": "null",
3140
+ "paceLength": 10,
3141
+ "percentage": false,
3142
+ "pointradius": 5,
3143
+ "points": false,
3144
+ "renderer": "flot",
3145
+ "seriesOverrides": [],
3146
+ "spaceLength": 10,
3147
+ "stack": false,
3148
+ "steppedLine": false,
3149
+ "targets": [
3150
+ {
3151
+ "expr": "delta(jvm_classes_loaded{application=\"$application\",instance=\"$instance\"}[5m]) or delta(jvm_classes_loaded_classes{application=\"$application\",instance=\"$instance\"}[5m])",
3152
+ "format": "time_series",
3153
+ "hide": false,
3154
+ "interval": "",
3155
+ "intervalFactor": 2,
3156
+ "legendFormat": "delta",
3157
+ "metric": "",
3158
+ "refId": "A",
3159
+ "step": 1200
3160
+ }
3161
+ ],
3162
+ "thresholds": [],
3163
+ "timeFrom": null,
3164
+ "timeRegions": [],
3165
+ "timeShift": null,
3166
+ "title": "Class delta (5m)",
3167
+ "tooltip": {
3168
+ "msResolution": false,
3169
+ "shared": true,
3170
+ "sort": 0,
3171
+ "value_type": "cumulative"
3172
+ },
3173
+ "type": "graph",
3174
+ "x-axis": true,
3175
+ "xaxis": {
3176
+ "buckets": null,
3177
+ "mode": "time",
3178
+ "name": null,
3179
+ "show": true,
3180
+ "values": []
3181
+ },
3182
+ "y-axis": true,
3183
+ "y_formats": ["ops", "short"],
3184
+ "yaxes": [
3185
+ {
3186
+ "decimals": null,
3187
+ "format": "short",
3188
+ "label": "",
3189
+ "logBase": 1,
3190
+ "max": null,
3191
+ "min": null,
3192
+ "show": true
3193
+ },
3194
+ {
3195
+ "format": "short",
3196
+ "label": null,
3197
+ "logBase": 1,
3198
+ "max": null,
3199
+ "min": null,
3200
+ "show": true
3201
+ }
3202
+ ],
3203
+ "yaxis": {
3204
+ "align": false,
3205
+ "alignLevel": null
3206
+ }
3207
+ },
3208
+ {
3209
+ "collapsed": false,
3210
+ "gridPos": {
3211
+ "h": 1,
3212
+ "w": 24,
3213
+ "x": 0,
3214
+ "y": 70
3215
+ },
3216
+ "id": 133,
3217
+ "panels": [],
3218
+ "repeat": null,
3219
+ "title": "Buffer Pools",
3220
+ "type": "row"
3221
+ },
3222
+ {
3223
+ "aliasColors": {},
3224
+ "bars": false,
3225
+ "dashLength": 10,
3226
+ "dashes": false,
3227
+ "datasource": "Prometheus",
3228
+ "editable": true,
3229
+ "error": false,
3230
+ "fill": 1,
3231
+ "grid": {
3232
+ "leftLogBase": 1,
3233
+ "leftMax": null,
3234
+ "leftMin": null,
3235
+ "rightLogBase": 1,
3236
+ "rightMax": null,
3237
+ "rightMin": null
3238
+ },
3239
+ "gridPos": {
3240
+ "h": 7,
3241
+ "w": 6,
3242
+ "x": 0,
3243
+ "y": 71
3244
+ },
3245
+ "id": 33,
3246
+ "legend": {
3247
+ "avg": false,
3248
+ "current": false,
3249
+ "max": false,
3250
+ "min": false,
3251
+ "show": true,
3252
+ "total": false,
3253
+ "values": false
3254
+ },
3255
+ "lines": true,
3256
+ "linewidth": 1,
3257
+ "links": [],
3258
+ "nullPointMode": "null",
3259
+ "paceLength": 10,
3260
+ "percentage": false,
3261
+ "pointradius": 5,
3262
+ "points": false,
3263
+ "renderer": "flot",
3264
+ "seriesOverrides": [],
3265
+ "spaceLength": 10,
3266
+ "stack": false,
3267
+ "steppedLine": false,
3268
+ "targets": [
3269
+ {
3270
+ "expr": "jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"direct\"}",
3271
+ "format": "time_series",
3272
+ "intervalFactor": 2,
3273
+ "legendFormat": "used",
3274
+ "metric": "",
3275
+ "refId": "A",
3276
+ "step": 2400
3277
+ },
3278
+ {
3279
+ "expr": "jvm_buffer_total_capacity_bytes{application=\"$application\", instance=\"$instance\", id=\"direct\"}",
3280
+ "format": "time_series",
3281
+ "intervalFactor": 2,
3282
+ "legendFormat": "capacity",
3283
+ "metric": "",
3284
+ "refId": "B",
3285
+ "step": 2400
3286
+ }
3287
+ ],
3288
+ "thresholds": [],
3289
+ "timeFrom": null,
3290
+ "timeRegions": [],
3291
+ "timeShift": null,
3292
+ "title": "Direct Buffers",
3293
+ "tooltip": {
3294
+ "msResolution": false,
3295
+ "shared": true,
3296
+ "sort": 0,
3297
+ "value_type": "cumulative"
3298
+ },
3299
+ "type": "graph",
3300
+ "x-axis": true,
3301
+ "xaxis": {
3302
+ "buckets": null,
3303
+ "mode": "time",
3304
+ "name": null,
3305
+ "show": true,
3306
+ "values": []
3307
+ },
3308
+ "y-axis": true,
3309
+ "y_formats": ["short", "short"],
3310
+ "yaxes": [
3311
+ {
3312
+ "format": "bytes",
3313
+ "label": null,
3314
+ "logBase": 1,
3315
+ "max": null,
3316
+ "min": 0,
3317
+ "show": true
3318
+ },
3319
+ {
3320
+ "format": "short",
3321
+ "label": null,
3322
+ "logBase": 1,
3323
+ "max": null,
3324
+ "min": null,
3325
+ "show": true
3326
+ }
3327
+ ],
3328
+ "yaxis": {
3329
+ "align": false,
3330
+ "alignLevel": null
3331
+ }
3332
+ },
3333
+ {
3334
+ "aliasColors": {},
3335
+ "bars": false,
3336
+ "dashLength": 10,
3337
+ "dashes": false,
3338
+ "datasource": "Prometheus",
3339
+ "editable": true,
3340
+ "error": false,
3341
+ "fill": 1,
3342
+ "grid": {
3343
+ "leftLogBase": 1,
3344
+ "leftMax": null,
3345
+ "leftMin": null,
3346
+ "rightLogBase": 1,
3347
+ "rightMax": null,
3348
+ "rightMin": null
3349
+ },
3350
+ "gridPos": {
3351
+ "h": 7,
3352
+ "w": 6,
3353
+ "x": 6,
3354
+ "y": 71
3355
+ },
3356
+ "id": 83,
3357
+ "legend": {
3358
+ "avg": false,
3359
+ "current": false,
3360
+ "max": false,
3361
+ "min": false,
3362
+ "show": true,
3363
+ "total": false,
3364
+ "values": false
3365
+ },
3366
+ "lines": true,
3367
+ "linewidth": 1,
3368
+ "links": [],
3369
+ "nullPointMode": "null",
3370
+ "paceLength": 10,
3371
+ "percentage": false,
3372
+ "pointradius": 5,
3373
+ "points": false,
3374
+ "renderer": "flot",
3375
+ "seriesOverrides": [],
3376
+ "spaceLength": 10,
3377
+ "stack": false,
3378
+ "steppedLine": false,
3379
+ "targets": [
3380
+ {
3381
+ "expr": "jvm_buffer_count{application=\"$application\", instance=\"$instance\", id=\"direct\"} or jvm_buffer_count_buffers{application=\"$application\", instance=\"$instance\", id=\"direct\"}",
3382
+ "format": "time_series",
3383
+ "intervalFactor": 2,
3384
+ "legendFormat": "count",
3385
+ "metric": "",
3386
+ "refId": "A",
3387
+ "step": 2400
3388
+ }
3389
+ ],
3390
+ "thresholds": [],
3391
+ "timeFrom": null,
3392
+ "timeRegions": [],
3393
+ "timeShift": null,
3394
+ "title": "Direct Buffers",
3395
+ "tooltip": {
3396
+ "msResolution": false,
3397
+ "shared": true,
3398
+ "sort": 0,
3399
+ "value_type": "cumulative"
3400
+ },
3401
+ "type": "graph",
3402
+ "x-axis": true,
3403
+ "xaxis": {
3404
+ "buckets": null,
3405
+ "mode": "time",
3406
+ "name": null,
3407
+ "show": true,
3408
+ "values": []
3409
+ },
3410
+ "y-axis": true,
3411
+ "y_formats": ["short", "short"],
3412
+ "yaxes": [
3413
+ {
3414
+ "decimals": 0,
3415
+ "format": "short",
3416
+ "label": null,
3417
+ "logBase": 1,
3418
+ "max": null,
3419
+ "min": 0,
3420
+ "show": true
3421
+ },
3422
+ {
3423
+ "format": "short",
3424
+ "label": null,
3425
+ "logBase": 1,
3426
+ "max": null,
3427
+ "min": null,
3428
+ "show": true
3429
+ }
3430
+ ],
3431
+ "yaxis": {
3432
+ "align": false,
3433
+ "alignLevel": null
3434
+ }
3435
+ },
3436
+ {
3437
+ "aliasColors": {},
3438
+ "bars": false,
3439
+ "dashLength": 10,
3440
+ "dashes": false,
3441
+ "datasource": "Prometheus",
3442
+ "editable": true,
3443
+ "error": false,
3444
+ "fill": 1,
3445
+ "grid": {
3446
+ "leftLogBase": 1,
3447
+ "leftMax": null,
3448
+ "leftMin": null,
3449
+ "rightLogBase": 1,
3450
+ "rightMax": null,
3451
+ "rightMin": null
3452
+ },
3453
+ "gridPos": {
3454
+ "h": 7,
3455
+ "w": 6,
3456
+ "x": 12,
3457
+ "y": 71
3458
+ },
3459
+ "id": 85,
3460
+ "legend": {
3461
+ "avg": false,
3462
+ "current": false,
3463
+ "max": false,
3464
+ "min": false,
3465
+ "show": true,
3466
+ "total": false,
3467
+ "values": false
3468
+ },
3469
+ "lines": true,
3470
+ "linewidth": 1,
3471
+ "links": [],
3472
+ "nullPointMode": "null",
3473
+ "paceLength": 10,
3474
+ "percentage": false,
3475
+ "pointradius": 5,
3476
+ "points": false,
3477
+ "renderer": "flot",
3478
+ "seriesOverrides": [],
3479
+ "spaceLength": 10,
3480
+ "stack": false,
3481
+ "steppedLine": false,
3482
+ "targets": [
3483
+ {
3484
+ "expr": "jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"mapped\"}",
3485
+ "format": "time_series",
3486
+ "intervalFactor": 2,
3487
+ "legendFormat": "used",
3488
+ "metric": "",
3489
+ "refId": "A",
3490
+ "step": 2400
3491
+ },
3492
+ {
3493
+ "expr": "jvm_buffer_total_capacity_bytes{application=\"$application\", instance=\"$instance\", id=\"mapped\"}",
3494
+ "format": "time_series",
3495
+ "intervalFactor": 2,
3496
+ "legendFormat": "capacity",
3497
+ "metric": "",
3498
+ "refId": "B",
3499
+ "step": 2400
3500
+ }
3501
+ ],
3502
+ "thresholds": [],
3503
+ "timeFrom": null,
3504
+ "timeRegions": [],
3505
+ "timeShift": null,
3506
+ "title": "Mapped Buffers",
3507
+ "tooltip": {
3508
+ "msResolution": false,
3509
+ "shared": true,
3510
+ "sort": 0,
3511
+ "value_type": "cumulative"
3512
+ },
3513
+ "type": "graph",
3514
+ "x-axis": true,
3515
+ "xaxis": {
3516
+ "buckets": null,
3517
+ "mode": "time",
3518
+ "name": null,
3519
+ "show": true,
3520
+ "values": []
3521
+ },
3522
+ "y-axis": true,
3523
+ "y_formats": ["short", "short"],
3524
+ "yaxes": [
3525
+ {
3526
+ "format": "bytes",
3527
+ "label": null,
3528
+ "logBase": 1,
3529
+ "max": null,
3530
+ "min": 0,
3531
+ "show": true
3532
+ },
3533
+ {
3534
+ "format": "short",
3535
+ "label": null,
3536
+ "logBase": 1,
3537
+ "max": null,
3538
+ "min": null,
3539
+ "show": true
3540
+ }
3541
+ ],
3542
+ "yaxis": {
3543
+ "align": false,
3544
+ "alignLevel": null
3545
+ }
3546
+ },
3547
+ {
3548
+ "aliasColors": {},
3549
+ "bars": false,
3550
+ "dashLength": 10,
3551
+ "dashes": false,
3552
+ "datasource": "Prometheus",
3553
+ "editable": true,
3554
+ "error": false,
3555
+ "fill": 1,
3556
+ "grid": {
3557
+ "leftLogBase": 1,
3558
+ "leftMax": null,
3559
+ "leftMin": null,
3560
+ "rightLogBase": 1,
3561
+ "rightMax": null,
3562
+ "rightMin": null
3563
+ },
3564
+ "gridPos": {
3565
+ "h": 7,
3566
+ "w": 6,
3567
+ "x": 18,
3568
+ "y": 71
3569
+ },
3570
+ "id": 84,
3571
+ "legend": {
3572
+ "avg": false,
3573
+ "current": false,
3574
+ "max": false,
3575
+ "min": false,
3576
+ "show": true,
3577
+ "total": false,
3578
+ "values": false
3579
+ },
3580
+ "lines": true,
3581
+ "linewidth": 1,
3582
+ "links": [],
3583
+ "nullPointMode": "null",
3584
+ "paceLength": 10,
3585
+ "percentage": false,
3586
+ "pointradius": 5,
3587
+ "points": false,
3588
+ "renderer": "flot",
3589
+ "seriesOverrides": [],
3590
+ "spaceLength": 10,
3591
+ "stack": false,
3592
+ "steppedLine": false,
3593
+ "targets": [
3594
+ {
3595
+ "expr": "jvm_buffer_count{application=\"$application\", instance=\"$instance\", id=\"mapped\"} or jvm_buffer_count_buffers{application=\"$application\", instance=\"$instance\", id=\"mapped\"}",
3596
+ "format": "time_series",
3597
+ "intervalFactor": 2,
3598
+ "legendFormat": "count",
3599
+ "metric": "",
3600
+ "refId": "A",
3601
+ "step": 2400
3602
+ }
3603
+ ],
3604
+ "thresholds": [],
3605
+ "timeFrom": null,
3606
+ "timeRegions": [],
3607
+ "timeShift": null,
3608
+ "title": "Mapped Buffers",
3609
+ "tooltip": {
3610
+ "msResolution": false,
3611
+ "shared": true,
3612
+ "sort": 0,
3613
+ "value_type": "cumulative"
3614
+ },
3615
+ "type": "graph",
3616
+ "x-axis": true,
3617
+ "xaxis": {
3618
+ "buckets": null,
3619
+ "mode": "time",
3620
+ "name": null,
3621
+ "show": true,
3622
+ "values": []
3623
+ },
3624
+ "y-axis": true,
3625
+ "y_formats": ["short", "short"],
3626
+ "yaxes": [
3627
+ {
3628
+ "decimals": 0,
3629
+ "format": "short",
3630
+ "label": null,
3631
+ "logBase": 1,
3632
+ "max": null,
3633
+ "min": 0,
3634
+ "show": true
3635
+ },
3636
+ {
3637
+ "format": "short",
3638
+ "label": null,
3639
+ "logBase": 1,
3640
+ "max": null,
3641
+ "min": null,
3642
+ "show": true
3643
+ }
3644
+ ],
3645
+ "yaxis": {
3646
+ "align": false,
3647
+ "alignLevel": null
3648
+ }
3649
+ }
3650
+ ],
3651
+ "refresh": "10s",
3652
+ "schemaVersion": 18,
3653
+ "style": "dark",
3654
+ "tags": [],
3655
+ "templating": {
3656
+ "list": [
3657
+ {
3658
+ "allValue": null,
3659
+ "current": {
3660
+ "text": "test",
3661
+ "value": "test"
3662
+ },
3663
+ "datasource": "Prometheus",
3664
+ "definition": "",
3665
+ "hide": 0,
3666
+ "includeAll": false,
3667
+ "label": "Application",
3668
+ "multi": false,
3669
+ "name": "application",
3670
+ "options": [],
3671
+ "query": "label_values(application)",
3672
+ "refresh": 2,
3673
+ "regex": "",
3674
+ "skipUrlSync": false,
3675
+ "sort": 0,
3676
+ "tagValuesQuery": "",
3677
+ "tags": [],
3678
+ "tagsQuery": "",
3679
+ "type": "query",
3680
+ "useTags": false
3681
+ },
3682
+ {
3683
+ "allFormat": "glob",
3684
+ "allValue": null,
3685
+ "current": {
3686
+ "text": "localhost:8080",
3687
+ "value": "localhost:8080"
3688
+ },
3689
+ "datasource": "Prometheus",
3690
+ "definition": "",
3691
+ "hide": 0,
3692
+ "includeAll": false,
3693
+ "label": "Instance",
3694
+ "multi": false,
3695
+ "multiFormat": "glob",
3696
+ "name": "instance",
3697
+ "options": [],
3698
+ "query": "label_values(jvm_memory_used_bytes{application=\"$application\"}, instance)",
3699
+ "refresh": 2,
3700
+ "regex": "",
3701
+ "skipUrlSync": false,
3702
+ "sort": 0,
3703
+ "tagValuesQuery": "",
3704
+ "tags": [],
3705
+ "tagsQuery": "",
3706
+ "type": "query",
3707
+ "useTags": false
3708
+ },
3709
+ {
3710
+ "allFormat": "glob",
3711
+ "allValue": null,
3712
+ "current": {
3713
+ "text": "All",
3714
+ "value": "$__all"
3715
+ },
3716
+ "datasource": "Prometheus",
3717
+ "definition": "",
3718
+ "hide": 0,
3719
+ "includeAll": true,
3720
+ "label": "JVM Memory Pools Heap",
3721
+ "multi": false,
3722
+ "multiFormat": "glob",
3723
+ "name": "jvm_memory_pool_heap",
3724
+ "options": [],
3725
+ "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"},id)",
3726
+ "refresh": 1,
3727
+ "regex": "",
3728
+ "skipUrlSync": false,
3729
+ "sort": 1,
3730
+ "tagValuesQuery": "",
3731
+ "tags": [],
3732
+ "tagsQuery": "",
3733
+ "type": "query",
3734
+ "useTags": false
3735
+ },
3736
+ {
3737
+ "allFormat": "glob",
3738
+ "allValue": null,
3739
+ "current": {
3740
+ "text": "All",
3741
+ "value": "$__all"
3742
+ },
3743
+ "datasource": "Prometheus",
3744
+ "definition": "",
3745
+ "hide": 0,
3746
+ "includeAll": true,
3747
+ "label": "JVM Memory Pools Non-Heap",
3748
+ "multi": false,
3749
+ "multiFormat": "glob",
3750
+ "name": "jvm_memory_pool_nonheap",
3751
+ "options": [],
3752
+ "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"},id)",
3753
+ "refresh": 1,
3754
+ "regex": "",
3755
+ "skipUrlSync": false,
3756
+ "sort": 2,
3757
+ "tagValuesQuery": "",
3758
+ "tags": [],
3759
+ "tagsQuery": "",
3760
+ "type": "query",
3761
+ "useTags": false
3762
+ }
3763
+ ]
3764
+ },
3765
+ "time": {
3766
+ "from": "now-30m",
3767
+ "to": "now"
3768
+ },
3769
+ "timepicker": {
3770
+ "now": true,
3771
+ "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
3772
+ "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
3773
+ },
3774
+ "timezone": "browser",
3775
+ "title": "JVM (Micrometer)",
3776
+ "uid": "Ud1CFe3iz",
3777
+ "version": 1
3778
+ }
src/main/docker/grafana/provisioning/dashboards/dashboard.yml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ apiVersion: 1
2
+
3
+ providers:
4
+ - name: 'Prometheus'
5
+ orgId: 1
6
+ folder: ''
7
+ type: file
8
+ disableDeletion: false
9
+ editable: true
10
+ options:
11
+ path: /etc/grafana/provisioning/dashboards
src/main/docker/grafana/provisioning/datasources/datasource.yml ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ apiVersion: 1
2
+
3
+ # list of datasources that should be deleted from the database
4
+ deleteDatasources:
5
+ - name: Prometheus
6
+ orgId: 1
7
+
8
+ # list of datasources to insert/update depending
9
+ # whats available in the database
10
+ datasources:
11
+ # <string, required> name of the datasource. Required
12
+ - name: Prometheus
13
+ # <string, required> datasource type. Required
14
+ type: prometheus
15
+ # <string, required> access mode. direct or proxy. Required
16
+ access: proxy
17
+ # <int> org id. will default to orgId 1 if not specified
18
+ orgId: 1
19
+ # <string> url
20
+ # On MacOS, replace localhost by host.docker.internal
21
+ url: http://localhost:9090
22
+ # <string> database password, if used
23
+ password:
24
+ # <string> database user, if used
25
+ user:
26
+ # <string> database name, if used
27
+ database:
28
+ # <bool> enable/disable basic auth
29
+ basicAuth: false
30
+ # <string> basic auth username
31
+ basicAuthUser: admin
32
+ # <string> basic auth password
33
+ basicAuthPassword: admin
34
+ # <bool> enable/disable with credentials headers
35
+ withCredentials:
36
+ # <bool> mark as default datasource. Max one per org
37
+ isDefault: true
38
+ # <map> fields that will be converted to json and stored in json_data
39
+ jsonData:
40
+ graphiteVersion: '1.1'
41
+ tlsAuth: false
42
+ tlsAuthWithCACert: false
43
+ # <string> json object of data that will be encrypted.
44
+ secureJsonData:
45
+ tlsCACert: '...'
46
+ tlsClientCert: '...'
47
+ tlsClientKey: '...'
48
+ version: 1
49
+ # <bool> allow users to edit datasources from the UI.
50
+ editable: true
src/main/docker/init-scripts/00-create-multiple-databases.sh ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ set -e
4
+ set -u
5
+
6
+ function create_user_and_database() {
7
+ local database=$1
8
+ echo " Creating user and database '$database'"
9
+ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL
10
+ CREATE DATABASE "$database";
11
+ GRANT ALL PRIVILEGES ON DATABASE "$database" TO "$POSTGRES_USER";
12
+ EOSQL
13
+ }
14
+
15
+ if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then
16
+ echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES"
17
+ for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do
18
+ create_user_and_database $db
19
+ echo "Initializing extensions for $db"
20
+ # Apply same extensions to test database
21
+ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$db" -f /docker-entrypoint-initdb.d/01-init-extensions.sql
22
+ done
23
+ echo "Multiple databases created and initialized"
24
+ fi
src/main/docker/init-scripts/01-init-extensions.sql ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- Enable required extensions
2
+ CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
3
+ CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
4
+ CREATE EXTENSION IF NOT EXISTS "pgcrypto";
5
+
6
+ -- Create schema if not exists
7
+ CREATE SCHEMA IF NOT EXISTS public;
8
+
9
+ -- Set search path
10
+ SET search_path TO public;
11
+
12
+ -- Create custom functions
13
+ CREATE OR REPLACE FUNCTION update_updated_at_column()
14
+ RETURNS TRIGGER AS $$
15
+ BEGIN
16
+ NEW.updated_at = CURRENT_TIMESTAMP;
17
+ RETURN NEW;
18
+ END;
19
+ $$ language 'plpgsql';
20
+
21
+ -- Grant necessary permissions
22
+ GRANT ALL PRIVILEGES ON DATABASE "da-discovery" TO "da-discovery";
23
+ GRANT ALL PRIVILEGES ON SCHEMA public TO "da-discovery";
24
+ GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "da-discovery";
25
+ GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO "da-discovery";
26
+
27
+ -- Set default privileges for future objects
28
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO "da-discovery";
29
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO "da-discovery";
30
+
31
+ -- Configure PostgreSQL settings
32
+ ALTER SYSTEM SET log_min_duration_statement = '1000';
33
+ ALTER SYSTEM SET log_connections = on;
34
+ ALTER SYSTEM SET log_disconnections = on;
35
+ ALTER SYSTEM SET log_statement = 'ddl';
36
+ ALTER SYSTEM SET log_temp_files = '0';
37
+ ALTER SYSTEM SET track_activities = on;
38
+ ALTER SYSTEM SET track_counts = on;
39
+ ALTER SYSTEM SET track_io_timing = on;
40
+ ALTER SYSTEM SET track_functions = 'all';
41
+ ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements';
42
+ ALTER SYSTEM SET pg_stat_statements.track = 'all';
src/main/docker/init-scripts/02-create-database-users.sql ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- Create application user if not exists
2
+ DO
3
+ $$
4
+ BEGIN
5
+ IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'app_user') THEN
6
+ CREATE USER app_user WITH PASSWORD 'app_password';
7
+ END IF;
8
+ END
9
+ $$;
10
+
11
+ -- Grant permissions to the main database
12
+ GRANT CONNECT ON DATABASE "da-discovery" TO app_user;
13
+ GRANT USAGE ON SCHEMA public TO app_user;
14
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
15
+ GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_user;
16
+
17
+ -- Set default privileges for future objects in main database
18
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
19
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE ON SEQUENCES TO app_user;
20
+
21
+ -- Connect to test database and set up the same permissions
22
+ \c da-discovery-test
23
+
24
+ -- Grant permissions to the test database
25
+ GRANT CONNECT ON DATABASE "da-discovery-test" TO app_user;
26
+ GRANT USAGE ON SCHEMA public TO app_user;
27
+ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
28
+ GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO app_user;
29
+
30
+ -- Set default privileges for future objects in test database
31
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_user;
32
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE ON SEQUENCES TO app_user;
src/main/docker/init-scripts/README.md ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PostgreSQL Initialization Scripts
2
+
3
+ This directory contains initialization scripts that are automatically executed when the PostgreSQL container starts.
4
+
5
+ ## Script Execution Order
6
+
7
+ Scripts are executed in alphabetical order:
8
+
9
+ 1. `00-create-multiple-databases.sh` - Creates both development and test databases
10
+ 2. `01-init-extensions.sql` - Initializes required PostgreSQL extensions (UUID, pgcrypto, etc.)
11
+ 3. `02-create-database-users.sql` - Creates application users and grants permissions
12
+
13
+ ## How It Works
14
+
15
+ When the PostgreSQL Docker container starts, it mounts this directory at `/docker-entrypoint-initdb.d/` inside the container.
16
+ The PostgreSQL Docker image is configured to automatically execute scripts found in this directory:
17
+
18
+ - `.sh` files are executed as shell scripts
19
+ - `.sql` files are executed as SQL commands
20
+ - `.sql.gz` files are decompressed and executed as SQL commands
21
+
22
+ ## Adding New Initialization Scripts
23
+
24
+ To add new initialization scripts:
25
+
26
+ 1. Create your script file with either `.sh` or `.sql` extension
27
+ 2. Use a numerical prefix to ensure proper execution order (e.g., `03-your-script.sql`)
28
+ 3. Make sure `.sh` scripts have execute permissions (`chmod +x your-script.sh`)
29
+ 4. Update the README.md file to document the purpose of your script
30
+
31
+ ## Important Notes
32
+
33
+ - Scripts in this directory are only executed when the database is first initialized
34
+ - To apply changes to an existing database, you'll need to use migrations (Liquibase)
35
+ - If you rebuild the container from scratch with new scripts, you'll lose all existing data
36
+
37
+ ## References
38
+
39
+ - [Postgres Docker entrypoint documentation](https://github.com/docker-library/postgres/blob/master/docker-entrypoint.sh)
40
+ - [Liquibase documentation](https://docs.liquibase.com/)
src/main/docker/init-scripts/run-postgresql.cmd ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+ echo Starting PostgreSQL for all environments (dev, test)...
3
+
4
+ REM Check if Docker is running
5
+ docker info > nul 2>&1
6
+ if %ERRORLEVEL% neq 0 (
7
+ echo Docker is not running! Please start Docker Desktop and try again.
8
+ exit /b 1
9
+ )
10
+
11
+ REM Create network if it doesn't exist
12
+ docker network inspect da-discovery-network > nul 2>&1
13
+ if %ERRORLEVEL% neq 0 (
14
+ echo Creating Docker network: da-discovery-network
15
+ docker network create da-discovery-network
16
+ )
17
+
18
+ REM Start PostgreSQL container
19
+ echo Starting PostgreSQL container...
20
+ pushd ..\
21
+ docker-compose -f postgresql.yml up -d
22
+ popd
23
+
24
+ REM Wait for PostgreSQL to be ready
25
+ echo Waiting for PostgreSQL to start up...
26
+ timeout /t 5 /nobreak > nul
27
+
28
+ REM Check if PostgreSQL is running
29
+ docker ps | find "postgresql"
30
+ if %ERRORLEVEL% neq 0 (
31
+ echo PostgreSQL failed to start!
32
+ exit /b 1
33
+ )
34
+
35
+ echo PostgreSQL is up and running with the following databases:
36
+ echo - da-discovery (Development)
37
+ echo - da-discovery-test (Testing)
38
+ echo.
39
+ echo Connection details:
40
+ echo Host: localhost
41
+ echo Port: 5432
42
+ echo Username: da-discovery
43
+ echo Password: da-discovery
44
+ echo.
45
+ echo You can now start the application with:
46
+ echo ./gradlew bootRun --args='--spring.profiles.active=dev'
47
+ echo.
48
+ echo To view initialization logs:
49
+ echo docker logs postgresql
50
+ echo.
51
+
52
+ exit /b 0
src/main/docker/init-scripts/run-postgresql.sh ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Exit on error
4
+ set -e
5
+
6
+ echo "Starting PostgreSQL for all environments (dev, test)..."
7
+
8
+ # Check if Docker is running
9
+ if ! docker info > /dev/null 2>&1; then
10
+ echo "Docker is not running! Please start Docker and try again."
11
+ exit 1
12
+ fi
13
+
14
+ # Create network if it doesn't exist
15
+ if ! docker network inspect da-discovery-network > /dev/null 2>&1; then
16
+ echo "Creating Docker network: da-discovery-network"
17
+ docker network create da-discovery-network
18
+ fi
19
+
20
+ # Start PostgreSQL container
21
+ echo "Starting PostgreSQL container..."
22
+ # Get the absolute path to the docker directory
23
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+ DOCKER_DIR="$(dirname "$SCRIPT_DIR")"
25
+ POSTGRESQL_YML="$DOCKER_DIR/postgresql.yml"
26
+
27
+ echo "Using PostgreSQL configuration from: $POSTGRESQL_YML"
28
+ docker-compose -f "$POSTGRESQL_YML" up -d
29
+
30
+ # Wait for PostgreSQL to be ready
31
+ echo "Waiting for PostgreSQL to start up..."
32
+ sleep 5
33
+
34
+ # Check if PostgreSQL is running
35
+ if ! docker ps | grep -q postgresql; then
36
+ echo "PostgreSQL failed to start!"
37
+ exit 1
38
+ fi
39
+
40
+ echo "PostgreSQL is up and running with the following databases:"
41
+ echo "- da-discovery (Development)"
42
+ echo "- da-discovery-test (Testing)"
43
+ echo ""
44
+ echo "Connection details:"
45
+ echo " Host: localhost"
46
+ echo " Port: 5432"
47
+ echo " Username: da-discovery"
48
+ echo " Password: da-discovery"
49
+ echo ""
50
+ echo "You can now start the application with:"
51
+ echo " ./gradlew bootRun --args='--spring.profiles.active=dev'"
52
+ echo ""
53
+ echo "To view initialization logs:"
54
+ echo " docker logs postgresql"
55
+ echo ""
56
+
57
+ exit 0
src/main/docker/jhipster-control-center.yml ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## How to use JHCC docker compose
2
+ # To allow JHCC to reach JHipster application from a docker container note that we set the host as host.docker.internal
3
+ # To reach the application from a browser, you need to add '127.0.0.1 host.docker.internal' to your hosts file.
4
+ ### Discovery mode
5
+ # JHCC support 3 kinds of discovery mode: Consul, Eureka and static
6
+ # In order to use one, please set SPRING_PROFILES_ACTIVE to one (and only one) of this values: consul,eureka,static
7
+ ### Discovery properties
8
+ # According to the discovery mode choose as Spring profile, you have to set the right properties
9
+ # please note that current properties are set to run JHCC with default values, personalize them if needed
10
+ # and remove those from other modes. You can only have one mode active.
11
+ #### Eureka
12
+ # - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:admin@host.docker.internal:8761/eureka/
13
+ #### Consul
14
+ # - SPRING_CLOUD_CONSUL_HOST=host.docker.internal
15
+ # - SPRING_CLOUD_CONSUL_PORT=8500
16
+ #### Static
17
+ # Add instances to "MyApp"
18
+ # - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYAPP_0_URI=http://host.docker.internal:8081
19
+ # - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYAPP_1_URI=http://host.docker.internal:8082
20
+ # Or add a new application named MyNewApp
21
+ # - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYNEWAPP_0_URI=http://host.docker.internal:8080
22
+ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production
23
+
24
+ #### IMPORTANT
25
+ # If you choose Consul or Eureka mode:
26
+ # Do not forget to remove the prefix "127.0.0.1" in front of their port in order to expose them.
27
+ # This is required because JHCC need to communicate with Consul or Eureka.
28
+ # - In Consul mode, the ports are in the consul.yml file.
29
+ # - In Eureka mode, the ports are in the jhipster-registry.yml file.
30
+
31
+ name: dgcrawler
32
+ services:
33
+ jhipster-control-center:
34
+ image: 'jhipster/jhipster-control-center:v0.5.0'
35
+ command:
36
+ - /bin/sh
37
+ - -c
38
+ # Patch /etc/hosts to support resolving host.docker.internal to the internal IP address used by the host in all OSes
39
+ - echo "`ip route | grep default | cut -d ' ' -f3` host.docker.internal" | tee -a /etc/hosts > /dev/null && java -jar /jhipster-control-center.jar
40
+ environment:
41
+ - _JAVA_OPTIONS=-Xmx512m -Xms256m
42
+ - SPRING_PROFILES_ACTIVE=prod,api-docs,none,oauth2
43
+ # For keycloak to work, you need to add '127.0.0.1 keycloak' to your hosts file
44
+ - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=http://keycloak:8080/realms/dalab
45
+ - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID=jhipster-control-center
46
+ - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET=jhipster-control-center
47
+ - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:admin@host.docker.internal:8761/eureka/
48
+ - LOGGING_FILE_NAME=/tmp/jhipster-control-center.log
49
+ # If you want to expose these ports outside your dev PC,
50
+ # remove the "127.0.0.1:" prefix
51
+ ports:
52
+ - 127.0.0.1:7419:7419
src/main/docker/jhipster-registry.yml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production
2
+ name: dgcrawler
3
+ services:
4
+ jhipster-registry:
5
+ image: jhipster/jhipster-registry:v7.4.0
6
+ volumes:
7
+ - ./central-server-config:/central-config
8
+ # When run with the "dev" Spring profile, the JHipster Registry will
9
+ # read the config from the local filesystem (central-server-config directory)
10
+ # When run with the "prod" Spring profile, it will read the configuration from a Git repository
11
+ # See https://www.jhipster.tech/jhipster-registry/#spring-cloud-config
12
+ environment:
13
+ - _JAVA_OPTIONS=-Xmx512m -Xms256m
14
+ - SPRING_PROFILES_ACTIVE=dev,api-docs,oauth2
15
+ - SPRING_SECURITY_USER_PASSWORD=admin
16
+ - JHIPSTER_REGISTRY_PASSWORD=admin
17
+ - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=native
18
+ - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_LOCATIONS=file:./central-config/localhost-config/
19
+ # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=git
20
+ # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_URI=https://github.com/jhipster/jhipster-registry/
21
+ # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_PATHS=central-config
22
+ # For keycloak to work, you need to add '127.0.0.1 keycloak' to your hosts file
23
+ - SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI=http://keycloak:8080/realms/dalab
24
+ - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID=jhipster-registry
25
+ - SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET=jhipster-registry
26
+ # If you want to expose these ports outside your dev PC,
27
+ # remove the "127.0.0.1:" prefix
28
+ ports:
29
+ - 127.0.0.1:8761:8761
30
+ healthcheck:
31
+ test: ['CMD', 'curl', '-f', 'http://localhost:8761/management/health']
32
+ interval: 5s
33
+ timeout: 5s
34
+ retries: 20
src/main/docker/jib/entrypoint.sh ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP}
4
+
5
+ # usage: file_env VAR [DEFAULT]
6
+ # ie: file_env 'XYZ_DB_PASSWORD' 'example'
7
+ # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
8
+ # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
9
+ file_env() {
10
+ local var="$1"
11
+ local fileVar="${var}_FILE"
12
+ local def="${2:-}"
13
+ if [[ ${!var:-} && ${!fileVar:-} ]]; then
14
+ echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
15
+ exit 1
16
+ fi
17
+ local val="$def"
18
+ if [[ ${!var:-} ]]; then
19
+ val="${!var}"
20
+ elif [[ ${!fileVar:-} ]]; then
21
+ val="$(< "${!fileVar}")"
22
+ fi
23
+
24
+ if [[ -n $val ]]; then
25
+ export "$var"="$val"
26
+ fi
27
+
28
+ unset "$fileVar"
29
+ }
30
+
31
+ file_env 'SPRING_DATASOURCE_URL'
32
+ file_env 'SPRING_DATASOURCE_USERNAME'
33
+ file_env 'SPRING_DATASOURCE_PASSWORD'
34
+ file_env 'SPRING_LIQUIBASE_URL'
35
+ file_env 'SPRING_LIQUIBASE_USER'
36
+ file_env 'SPRING_LIQUIBASE_PASSWORD'
37
+ file_env 'JHIPSTER_REGISTRY_PASSWORD'
38
+
39
+ exec java ${JAVA_OPTS} -noverify -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* "com.dalab.discovery.application.DADiscoveryAgent" "$@"
src/main/docker/kafka.yml ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ # Single Kafka container with KRaft mode (no Zookeeper needed)
5
+ kafka:
6
+ image: confluentinc/cp-kafka:7.5.3
7
+ container_name: kafka
8
+ ports:
9
+ - '9092:9092'
10
+ environment:
11
+ # KRaft mode configuration
12
+ KAFKA_NODE_ID: 1
13
+ KAFKA_PROCESS_ROLES: 'broker,controller'
14
+ KAFKA_CONTROLLER_QUORUM_VOTERS: '1@kafka:29093'
15
+ KAFKA_LISTENERS: 'PLAINTEXT://kafka:29092,CONTROLLER://kafka:29093,PLAINTEXT_HOST://0.0.0.0:9092'
16
+ KAFKA_INTER_BROKER_LISTENER_NAME: 'PLAINTEXT'
17
+ KAFKA_ADVERTISED_LISTENERS: 'PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092'
18
+ KAFKA_CONTROLLER_LISTENER_NAMES: 'CONTROLLER'
19
+ KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: 'CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT'
20
+
21
+ # Cluster settings for KRaft
22
+ KAFKA_CLUSTER_ID: 'MkU3OEVBNTcwNTJENDM2Qk'
23
+
24
+ # Topic and replication settings (single node)
25
+ KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
26
+ KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
27
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
28
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
29
+ KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
30
+ KAFKA_NUM_PARTITIONS: 3
31
+ KAFKA_DEFAULT_REPLICATION_FACTOR: 1
32
+ KAFKA_MIN_INSYNC_REPLICAS: 1
33
+
34
+ # Performance and retention settings
35
+ KAFKA_LOG_RETENTION_HOURS: 168
36
+ KAFKA_LOG_SEGMENT_BYTES: 1073741824
37
+ KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: 'false'
38
+
39
+ # Memory settings
40
+ KAFKA_HEAP_OPTS: '-Xmx512M -Xms512M'
41
+
42
+ # Log directories for KRaft
43
+ KAFKA_LOG_DIRS: '/var/lib/kafka/data'
44
+ volumes:
45
+ - kafka-kraft-data:/var/lib/kafka/data
46
+ networks:
47
+ - da-discovery-network
48
+ healthcheck:
49
+ test: ['CMD', 'kafka-topics', '--bootstrap-server', 'localhost:9092', '--list']
50
+ interval: 30s
51
+ timeout: 10s
52
+ retries: 5
53
+
54
+ networks:
55
+ da-discovery-network:
56
+ name: da-discovery-network
57
+
58
+ volumes:
59
+ kafka-kraft-data:
60
+ driver: local
src/main/docker/keycloak.yml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production
2
+ name: dgcrawler
3
+ services:
4
+ keycloak:
5
+ image: quay.io/keycloak/keycloak:23.0.1
6
+ command: 'start-dev --import-realm'
7
+ volumes:
8
+ - ./realm-config:/opt/keycloak/data/import
9
+ - ./realm-config/keycloak-health-check.sh:/opt/keycloak/health-check.sh
10
+ environment:
11
+ - KC_DB=dev-file
12
+ - KEYCLOAK_ADMIN=admin
13
+ - KEYCLOAK_ADMIN_PASSWORD=admin
14
+ - KC_FEATURES=scripts
15
+ - KC_HTTP_PORT=9080
16
+ - KC_HTTPS_PORT=9443
17
+ - KC_HEALTH_ENABLED=true
18
+ # If you want to expose these ports outside your dev PC,
19
+ # remove the "127.0.0.1:" prefix
20
+ ports:
21
+ - 127.0.0.1:9080:9080
22
+ - 127.0.0.1:9443:9443
23
+ healthcheck:
24
+ test: 'bash /opt/keycloak/health-check.sh'
25
+ interval: 5s
26
+ timeout: 5s
27
+ retries: 20
28
+ start_period: 10s
src/main/docker/monitoring.yml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production
2
+ name: dgcrawler
3
+ services:
4
+ prometheus:
5
+ image: prom/prometheus:v2.48.0
6
+ volumes:
7
+ - ./prometheus/:/etc/prometheus/
8
+ command:
9
+ - '--config.file=/etc/prometheus/prometheus.yml'
10
+ # If you want to expose these ports outside your dev PC,
11
+ # remove the "127.0.0.1:" prefix
12
+ ports:
13
+ - 127.0.0.1:9090:9090
14
+ # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and
15
+ # grafana/provisioning/datasources/datasource.yml
16
+ network_mode: 'host' # to test locally running service
17
+ grafana:
18
+ image: grafana/grafana:10.2.2
19
+ volumes:
20
+ - ./grafana/provisioning/:/etc/grafana/provisioning/
21
+ environment:
22
+ - GF_SECURITY_ADMIN_PASSWORD=admin
23
+ - GF_USERS_ALLOW_SIGN_UP=false
24
+ - GF_INSTALL_PLUGINS=grafana-piechart-panel
25
+ # If you want to expose these ports outside your dev PC,
26
+ # remove the "127.0.0.1:" prefix
27
+ ports:
28
+ - 127.0.0.1:3000:3000
29
+ # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and
30
+ # grafana/provisioning/datasources/datasource.yml
31
+ network_mode: 'host' # to test locally running service
src/main/docker/postgresql.yml ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+ services:
3
+ postgresql:
4
+ image: postgres:15.1
5
+ volumes:
6
+ - postgresql-data:/var/lib/postgresql/data
7
+ - ./init-scripts:/docker-entrypoint-initdb.d
8
+ environment:
9
+ - POSTGRES_USER=da-discovery
10
+ - POSTGRES_PASSWORD=da-discovery
11
+ - POSTGRES_DB=da-discovery
12
+ - POSTGRES_MULTIPLE_DATABASES=da-discovery-test
13
+ # PostgreSQL optimization settings
14
+ - POSTGRES_INITDB_ARGS=--data-checksums
15
+ # Performance tuning
16
+ - POSTGRES_MAX_CONNECTIONS=100
17
+ - POSTGRES_SHARED_BUFFERS=256MB
18
+ - POSTGRES_EFFECTIVE_CACHE_SIZE=768MB
19
+ - POSTGRES_MAINTENANCE_WORK_MEM=64MB
20
+ - POSTGRES_WORK_MEM=4MB
21
+ ports:
22
+ - "5432:5432"
23
+ healthcheck:
24
+ test: ["CMD-SHELL", "pg_isready -U da-discovery -d da-discovery"]
25
+ interval: 5s
26
+ timeout: 5s
27
+ retries: 5
28
+ command:
29
+ - "postgres"
30
+ - "-c"
31
+ - "max_connections=100"
32
+ - "-c"
33
+ - "shared_buffers=256MB"
34
+ - "-c"
35
+ - "effective_cache_size=768MB"
36
+ - "-c"
37
+ - "maintenance_work_mem=64MB"
38
+ - "-c"
39
+ - "work_mem=4MB"
40
+ - "-c"
41
+ - "log_min_duration_statement=1000"
42
+ - "-c"
43
+ - "log_connections=on"
44
+ - "-c"
45
+ - "log_disconnections=on"
46
+ restart: unless-stopped
47
+ networks:
48
+ - da-discovery-network
49
+
50
+ volumes:
51
+ postgresql-data:
52
+ driver: local
53
+
54
+ networks:
55
+ da-discovery-network:
56
+ external: true
src/main/docker/prometheus/prometheus.yml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Sample global config for monitoring JHipster applications
2
+ global:
3
+ scrape_interval: 15s # By default, scrape targets every 15 seconds.
4
+ evaluation_interval: 15s # By default, scrape targets every 15 seconds.
5
+ # scrape_timeout is set to the global default (10s).
6
+
7
+ # Attach these labels to any time series or alerts when communicating with
8
+ # external systems (federation, remote storage, Alertmanager).
9
+ external_labels:
10
+ monitor: 'jhipster'
11
+
12
+ # A scrape configuration containing exactly one endpoint to scrape:
13
+ # Here it's Prometheus itself.
14
+ scrape_configs:
15
+ # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
16
+ - job_name: 'prometheus'
17
+
18
+ # Override the global default and scrape targets from this job every 5 seconds.
19
+ scrape_interval: 5s
20
+
21
+ # scheme defaults to 'http' enable https in case your application is server via https
22
+ #scheme: https
23
+ # basic auth is not needed by default. See https://www.jhipster.tech/monitoring/#configuring-metrics-forwarding for details
24
+ #basic_auth:
25
+ # username: admin
26
+ # password: admin
27
+ metrics_path: /management/prometheus
28
+ static_configs:
29
+ - targets:
30
+ # On MacOS, replace localhost by host.docker.internal
31
+ - localhost:8081
src/main/docker/realm-config/jhipster-realm.json ADDED
@@ -0,0 +1,2351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "id": "jhipster",
3
+ "realm": "jhipster",
4
+ "displayName": "JHipster",
5
+ "displayNameHtml": "<div class=\"kc-logo-text\"><span>JHipster</span></div>",
6
+ "notBefore": 0,
7
+ "defaultSignatureAlgorithm": "RS256",
8
+ "revokeRefreshToken": false,
9
+ "refreshTokenMaxReuse": 0,
10
+ "accessTokenLifespan": 300,
11
+ "accessTokenLifespanForImplicitFlow": 900,
12
+ "ssoSessionIdleTimeout": 1800,
13
+ "ssoSessionMaxLifespan": 36000,
14
+ "ssoSessionIdleTimeoutRememberMe": 0,
15
+ "ssoSessionMaxLifespanRememberMe": 0,
16
+ "offlineSessionIdleTimeout": 2592000,
17
+ "offlineSessionMaxLifespanEnabled": false,
18
+ "offlineSessionMaxLifespan": 5184000,
19
+ "clientSessionIdleTimeout": 0,
20
+ "clientSessionMaxLifespan": 0,
21
+ "clientOfflineSessionIdleTimeout": 0,
22
+ "clientOfflineSessionMaxLifespan": 0,
23
+ "accessCodeLifespan": 60,
24
+ "accessCodeLifespanUserAction": 300,
25
+ "accessCodeLifespanLogin": 1800,
26
+ "actionTokenGeneratedByAdminLifespan": 43200,
27
+ "actionTokenGeneratedByUserLifespan": 300,
28
+ "oauth2DeviceCodeLifespan": 600,
29
+ "oauth2DevicePollingInterval": 5,
30
+ "enabled": true,
31
+ "sslRequired": "external",
32
+ "registrationAllowed": false,
33
+ "registrationEmailAsUsername": false,
34
+ "rememberMe": false,
35
+ "verifyEmail": false,
36
+ "loginWithEmailAllowed": true,
37
+ "duplicateEmailsAllowed": false,
38
+ "resetPasswordAllowed": false,
39
+ "editUsernameAllowed": false,
40
+ "bruteForceProtected": false,
41
+ "permanentLockout": false,
42
+ "maxFailureWaitSeconds": 900,
43
+ "minimumQuickLoginWaitSeconds": 60,
44
+ "waitIncrementSeconds": 60,
45
+ "quickLoginCheckMilliSeconds": 1000,
46
+ "maxDeltaTimeSeconds": 43200,
47
+ "failureFactor": 30,
48
+ "roles": {
49
+ "realm": [
50
+ {
51
+ "id": "8e986fb5-dafb-43bf-a7c2-7e57572d3d80",
52
+ "name": "ROLE_ADMIN",
53
+ "description": "Jhipster administrator role",
54
+ "composite": false,
55
+ "clientRole": false,
56
+ "containerId": "jhipster",
57
+ "attributes": {}
58
+ },
59
+ {
60
+ "id": "e1b19afd-f612-4a79-bdf8-26a99b89b10b",
61
+ "name": "offline_access",
62
+ "description": "${role_offline-access}",
63
+ "composite": false,
64
+ "clientRole": false,
65
+ "containerId": "jhipster",
66
+ "attributes": {}
67
+ },
68
+ {
69
+ "id": "ec5705e1-fc1d-4d21-8364-abd3bd4efcd0",
70
+ "name": "ROLE_USER",
71
+ "description": "Jhipster user role",
72
+ "composite": false,
73
+ "clientRole": false,
74
+ "containerId": "jhipster",
75
+ "attributes": {}
76
+ },
77
+ {
78
+ "id": "d2b73e7b-a2d7-40e9-8ebc-2af00454e8aa",
79
+ "name": "default-roles-jhipster",
80
+ "description": "${role_default-roles}",
81
+ "composite": true,
82
+ "composites": {
83
+ "realm": ["offline_access", "uma_authorization"],
84
+ "client": {
85
+ "account": ["view-profile", "manage-account"]
86
+ }
87
+ },
88
+ "clientRole": false,
89
+ "containerId": "jhipster",
90
+ "attributes": {}
91
+ },
92
+ {
93
+ "id": "2eec61d0-9581-4dbf-8c7b-f32dc5fac3ce",
94
+ "name": "uma_authorization",
95
+ "description": "${role_uma_authorization}",
96
+ "composite": false,
97
+ "clientRole": false,
98
+ "containerId": "jhipster",
99
+ "attributes": {}
100
+ }
101
+ ],
102
+ "client": {
103
+ "realm-management": [
104
+ {
105
+ "id": "a6249a12-d76c-4514-b137-e3018b243e25",
106
+ "name": "manage-authorization",
107
+ "description": "${role_manage-authorization}",
108
+ "composite": false,
109
+ "clientRole": true,
110
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
111
+ "attributes": {}
112
+ },
113
+ {
114
+ "id": "a28bc401-c5ad-4fab-aef4-42629988c10b",
115
+ "name": "view-realm",
116
+ "description": "${role_view-realm}",
117
+ "composite": false,
118
+ "clientRole": true,
119
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
120
+ "attributes": {}
121
+ },
122
+ {
123
+ "id": "464bca1f-136f-45de-a7fc-b976a185ce7e",
124
+ "name": "view-users",
125
+ "description": "${role_view-users}",
126
+ "composite": true,
127
+ "composites": {
128
+ "client": {
129
+ "realm-management": ["query-users", "query-groups"]
130
+ }
131
+ },
132
+ "clientRole": true,
133
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
134
+ "attributes": {}
135
+ },
136
+ {
137
+ "id": "98c2fa77-d3c8-4f68-b9f4-b79f87efd4a9",
138
+ "name": "query-users",
139
+ "description": "${role_query-users}",
140
+ "composite": false,
141
+ "clientRole": true,
142
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
143
+ "attributes": {}
144
+ },
145
+ {
146
+ "id": "6b82bfdb-c8de-4274-95b4-a683eb4ead50",
147
+ "name": "view-identity-providers",
148
+ "description": "${role_view-identity-providers}",
149
+ "composite": false,
150
+ "clientRole": true,
151
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
152
+ "attributes": {}
153
+ },
154
+ {
155
+ "id": "3c6b9cfe-80c4-41d5-a5ac-0cadebacfc8d",
156
+ "name": "manage-identity-providers",
157
+ "description": "${role_manage-identity-providers}",
158
+ "composite": false,
159
+ "clientRole": true,
160
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
161
+ "attributes": {}
162
+ },
163
+ {
164
+ "id": "23676fb8-235a-4e54-a0d0-9bed1ccbe2f8",
165
+ "name": "query-groups",
166
+ "description": "${role_query-groups}",
167
+ "composite": false,
168
+ "clientRole": true,
169
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
170
+ "attributes": {}
171
+ },
172
+ {
173
+ "id": "b71fe952-bb06-4e4a-91ef-2d2714f770e1",
174
+ "name": "impersonation",
175
+ "description": "${role_impersonation}",
176
+ "composite": false,
177
+ "clientRole": true,
178
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
179
+ "attributes": {}
180
+ },
181
+ {
182
+ "id": "0813cbd0-c73d-469d-a54d-84a865c302af",
183
+ "name": "manage-events",
184
+ "description": "${role_manage-events}",
185
+ "composite": false,
186
+ "clientRole": true,
187
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
188
+ "attributes": {}
189
+ },
190
+ {
191
+ "id": "c7a4f4c1-9089-458c-a765-f6d22ea94690",
192
+ "name": "view-authorization",
193
+ "description": "${role_view-authorization}",
194
+ "composite": false,
195
+ "clientRole": true,
196
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
197
+ "attributes": {}
198
+ },
199
+ {
200
+ "id": "2e1bc884-e9d3-45d2-909c-2777a78ca8ae",
201
+ "name": "manage-realm",
202
+ "description": "${role_manage-realm}",
203
+ "composite": false,
204
+ "clientRole": true,
205
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
206
+ "attributes": {}
207
+ },
208
+ {
209
+ "id": "0a05451e-7d64-4e87-b585-f1143ce5752e",
210
+ "name": "query-clients",
211
+ "description": "${role_query-clients}",
212
+ "composite": false,
213
+ "clientRole": true,
214
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
215
+ "attributes": {}
216
+ },
217
+ {
218
+ "id": "dfad4d08-6d75-42b6-8699-4886e47bc464",
219
+ "name": "view-events",
220
+ "description": "${role_view-events}",
221
+ "composite": false,
222
+ "clientRole": true,
223
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
224
+ "attributes": {}
225
+ },
226
+ {
227
+ "id": "392ed0a3-f6ad-48a1-b201-648037d2b4bd",
228
+ "name": "realm-admin",
229
+ "description": "${role_realm-admin}",
230
+ "composite": true,
231
+ "composites": {
232
+ "client": {
233
+ "realm-management": [
234
+ "manage-authorization",
235
+ "view-realm",
236
+ "view-users",
237
+ "query-users",
238
+ "manage-identity-providers",
239
+ "view-identity-providers",
240
+ "query-groups",
241
+ "impersonation",
242
+ "manage-events",
243
+ "query-clients",
244
+ "manage-realm",
245
+ "view-authorization",
246
+ "view-events",
247
+ "view-clients",
248
+ "create-client",
249
+ "manage-clients",
250
+ "manage-users",
251
+ "query-realms"
252
+ ]
253
+ }
254
+ },
255
+ "clientRole": true,
256
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
257
+ "attributes": {}
258
+ },
259
+ {
260
+ "id": "d7efdf61-affb-42a1-bcb0-b2c30d87a39e",
261
+ "name": "view-clients",
262
+ "description": "${role_view-clients}",
263
+ "composite": true,
264
+ "composites": {
265
+ "client": {
266
+ "realm-management": ["query-clients"]
267
+ }
268
+ },
269
+ "clientRole": true,
270
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
271
+ "attributes": {}
272
+ },
273
+ {
274
+ "id": "14da8e56-5c8b-4764-96da-250449a32fd4",
275
+ "name": "create-client",
276
+ "description": "${role_create-client}",
277
+ "composite": false,
278
+ "clientRole": true,
279
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
280
+ "attributes": {}
281
+ },
282
+ {
283
+ "id": "88e6a9f5-259c-487d-af35-2a98da066816",
284
+ "name": "manage-clients",
285
+ "description": "${role_manage-clients}",
286
+ "composite": false,
287
+ "clientRole": true,
288
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
289
+ "attributes": {}
290
+ },
291
+ {
292
+ "id": "932273a7-c02b-43db-81c5-96a0dc45e454",
293
+ "name": "manage-users",
294
+ "description": "${role_manage-users}",
295
+ "composite": false,
296
+ "clientRole": true,
297
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
298
+ "attributes": {}
299
+ },
300
+ {
301
+ "id": "e3edf335-cec5-4012-a00d-fcac045052e1",
302
+ "name": "query-realms",
303
+ "description": "${role_query-realms}",
304
+ "composite": false,
305
+ "clientRole": true,
306
+ "containerId": "898488c8-e260-41c5-a463-7ceea14d587a",
307
+ "attributes": {}
308
+ }
309
+ ],
310
+ "jhipster-control-center": [],
311
+ "security-admin-console": [],
312
+ "web_app": [],
313
+ "admin-cli": [],
314
+ "account-console": [],
315
+ "jhipster-registry": [],
316
+ "broker": [
317
+ {
318
+ "id": "5b08a930-9f1d-4030-ae75-92c1e4c9352c",
319
+ "name": "read-token",
320
+ "description": "${role_read-token}",
321
+ "composite": false,
322
+ "clientRole": true,
323
+ "containerId": "88e1225b-f0b9-46ba-8efd-f2c10ce23058",
324
+ "attributes": {}
325
+ }
326
+ ],
327
+ "account": [
328
+ {
329
+ "id": "a88c56b8-6bc9-418a-92bc-7a17e7707f60",
330
+ "name": "view-profile",
331
+ "description": "${role_view-profile}",
332
+ "composite": false,
333
+ "clientRole": true,
334
+ "containerId": "6cc5a716-0880-47dc-b714-9a4967246b2f",
335
+ "attributes": {}
336
+ },
337
+ {
338
+ "id": "0cb954ab-987f-482a-b2d7-0d481ba1d532",
339
+ "name": "view-applications",
340
+ "description": "${role_view-applications}",
341
+ "composite": false,
342
+ "clientRole": true,
343
+ "containerId": "6cc5a716-0880-47dc-b714-9a4967246b2f",
344
+ "attributes": {}
345
+ },
346
+ {
347
+ "id": "6450156d-7526-48f2-8ea0-bb1e51f9eefa",
348
+ "name": "manage-account",
349
+ "description": "${role_manage-account}",
350
+ "composite": true,
351
+ "composites": {
352
+ "client": {
353
+ "account": ["manage-account-links"]
354
+ }
355
+ },
356
+ "clientRole": true,
357
+ "containerId": "6cc5a716-0880-47dc-b714-9a4967246b2f",
358
+ "attributes": {}
359
+ },
360
+ {
361
+ "id": "e5b2ba76-4c36-4ba1-b210-89a1ac3c6bbe",
362
+ "name": "view-consent",
363
+ "description": "${role_view-consent}",
364
+ "composite": false,
365
+ "clientRole": true,
366
+ "containerId": "6cc5a716-0880-47dc-b714-9a4967246b2f",
367
+ "attributes": {}
368
+ },
369
+ {
370
+ "id": "35537940-67a6-4217-881b-1ff98109b374",
371
+ "name": "manage-consent",
372
+ "description": "${role_manage-consent}",
373
+ "composite": true,
374
+ "composites": {
375
+ "client": {
376
+ "account": ["view-consent"]
377
+ }
378
+ },
379
+ "clientRole": true,
380
+ "containerId": "6cc5a716-0880-47dc-b714-9a4967246b2f",
381
+ "attributes": {}
382
+ },
383
+ {
384
+ "id": "5ebf404b-7805-4da2-abb4-9db7d3b36120",
385
+ "name": "delete-account",
386
+ "description": "${role_delete-account}",
387
+ "composite": false,
388
+ "clientRole": true,
389
+ "containerId": "6cc5a716-0880-47dc-b714-9a4967246b2f",
390
+ "attributes": {}
391
+ },
392
+ {
393
+ "id": "16925eed-a410-4241-9af8-cc7992c42f7a",
394
+ "name": "view-groups",
395
+ "description": "${role_view-groups}",
396
+ "composite": false,
397
+ "clientRole": true,
398
+ "containerId": "6cc5a716-0880-47dc-b714-9a4967246b2f",
399
+ "attributes": {}
400
+ },
401
+ {
402
+ "id": "a7f45fab-19c3-4c48-aca3-85f828ca0fed",
403
+ "name": "manage-account-links",
404
+ "description": "${role_manage-account-links}",
405
+ "composite": false,
406
+ "clientRole": true,
407
+ "containerId": "6cc5a716-0880-47dc-b714-9a4967246b2f",
408
+ "attributes": {}
409
+ }
410
+ ]
411
+ }
412
+ },
413
+ "groups": [
414
+ {
415
+ "id": "afb0c768-ab0f-454c-a8ea-bc9e70b50248",
416
+ "name": "Admins",
417
+ "path": "/Admins",
418
+ "attributes": {},
419
+ "realmRoles": ["ROLE_ADMIN"],
420
+ "clientRoles": {},
421
+ "subGroups": []
422
+ },
423
+ {
424
+ "id": "672767bb-4ab0-4d37-93a1-9b6c2416b6b2",
425
+ "name": "Users",
426
+ "path": "/Users",
427
+ "attributes": {},
428
+ "realmRoles": ["ROLE_USER"],
429
+ "clientRoles": {},
430
+ "subGroups": []
431
+ }
432
+ ],
433
+ "defaultRole": {
434
+ "id": "d2b73e7b-a2d7-40e9-8ebc-2af00454e8aa",
435
+ "name": "default-roles-jhipster",
436
+ "description": "${role_default-roles}",
437
+ "composite": true,
438
+ "clientRole": false,
439
+ "containerId": "jhipster"
440
+ },
441
+ "requiredCredentials": ["password"],
442
+ "otpPolicyType": "totp",
443
+ "otpPolicyAlgorithm": "HmacSHA1",
444
+ "otpPolicyInitialCounter": 0,
445
+ "otpPolicyDigits": 6,
446
+ "otpPolicyLookAheadWindow": 1,
447
+ "otpPolicyPeriod": 30,
448
+ "otpPolicyCodeReusable": false,
449
+ "otpSupportedApplications": ["totpAppGoogleName", "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName"],
450
+ "localizationTexts": {},
451
+ "webAuthnPolicyRpEntityName": "keycloak",
452
+ "webAuthnPolicySignatureAlgorithms": ["ES256"],
453
+ "webAuthnPolicyRpId": "",
454
+ "webAuthnPolicyAttestationConveyancePreference": "not specified",
455
+ "webAuthnPolicyAuthenticatorAttachment": "not specified",
456
+ "webAuthnPolicyRequireResidentKey": "not specified",
457
+ "webAuthnPolicyUserVerificationRequirement": "not specified",
458
+ "webAuthnPolicyCreateTimeout": 0,
459
+ "webAuthnPolicyAvoidSameAuthenticatorRegister": false,
460
+ "webAuthnPolicyAcceptableAaguids": [],
461
+ "webAuthnPolicyExtraOrigins": [],
462
+ "webAuthnPolicyPasswordlessRpEntityName": "keycloak",
463
+ "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"],
464
+ "webAuthnPolicyPasswordlessRpId": "",
465
+ "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified",
466
+ "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified",
467
+ "webAuthnPolicyPasswordlessRequireResidentKey": "not specified",
468
+ "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified",
469
+ "webAuthnPolicyPasswordlessCreateTimeout": 0,
470
+ "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false,
471
+ "webAuthnPolicyPasswordlessAcceptableAaguids": [],
472
+ "webAuthnPolicyPasswordlessExtraOrigins": [],
473
+ "users": [
474
+ {
475
+ "id": "f742ba6f-1d8a-4dec-bf15-e02dab508283",
476
+ "createdTimestamp": 1598681172054,
477
+ "username": "service-account-internal",
478
+ "enabled": true,
479
+ "totp": false,
480
+ "emailVerified": false,
481
+ "serviceAccountClientId": "internal",
482
+ "disableableCredentialTypes": [],
483
+ "requiredActions": [],
484
+ "realmRoles": ["default-roles-jhipster"],
485
+ "notBefore": 0,
486
+ "groups": []
487
+ }
488
+ ],
489
+ "scopeMappings": [
490
+ {
491
+ "clientScope": "offline_access",
492
+ "roles": ["offline_access"]
493
+ }
494
+ ],
495
+ "clientScopeMappings": {
496
+ "account": [
497
+ {
498
+ "client": "account-console",
499
+ "roles": ["manage-account", "view-groups"]
500
+ }
501
+ ]
502
+ },
503
+ "clients": [
504
+ {
505
+ "id": "6cc5a716-0880-47dc-b714-9a4967246b2f",
506
+ "clientId": "account",
507
+ "name": "${client_account}",
508
+ "rootUrl": "${authBaseUrl}",
509
+ "baseUrl": "/realms/jhipster/account/",
510
+ "surrogateAuthRequired": false,
511
+ "enabled": true,
512
+ "alwaysDisplayInConsole": false,
513
+ "clientAuthenticatorType": "client-secret",
514
+ "secret": "a2260b9e-5a23-41a0-a777-7e69891109e2",
515
+ "redirectUris": ["/realms/jhipster/account/*"],
516
+ "webOrigins": [],
517
+ "notBefore": 0,
518
+ "bearerOnly": false,
519
+ "consentRequired": false,
520
+ "standardFlowEnabled": true,
521
+ "implicitFlowEnabled": false,
522
+ "directAccessGrantsEnabled": false,
523
+ "serviceAccountsEnabled": false,
524
+ "publicClient": false,
525
+ "frontchannelLogout": false,
526
+ "protocol": "openid-connect",
527
+ "attributes": {
528
+ "post.logout.redirect.uris": "+"
529
+ },
530
+ "authenticationFlowBindingOverrides": {},
531
+ "fullScopeAllowed": false,
532
+ "nodeReRegistrationTimeout": 0,
533
+ "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "email"],
534
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
535
+ },
536
+ {
537
+ "id": "fb0a4870-06db-4f9d-9d44-baf51a00cc34",
538
+ "clientId": "account-console",
539
+ "name": "${client_account-console}",
540
+ "rootUrl": "${authBaseUrl}",
541
+ "baseUrl": "/realms/jhipster/account/",
542
+ "surrogateAuthRequired": false,
543
+ "enabled": true,
544
+ "alwaysDisplayInConsole": false,
545
+ "clientAuthenticatorType": "client-secret",
546
+ "secret": "b7c957d3-590f-49fb-aacb-65d20269c558",
547
+ "redirectUris": ["/realms/jhipster/account/*"],
548
+ "webOrigins": [],
549
+ "notBefore": 0,
550
+ "bearerOnly": false,
551
+ "consentRequired": false,
552
+ "standardFlowEnabled": true,
553
+ "implicitFlowEnabled": false,
554
+ "directAccessGrantsEnabled": false,
555
+ "serviceAccountsEnabled": false,
556
+ "publicClient": true,
557
+ "frontchannelLogout": false,
558
+ "protocol": "openid-connect",
559
+ "attributes": {
560
+ "post.logout.redirect.uris": "+",
561
+ "pkce.code.challenge.method": "S256"
562
+ },
563
+ "authenticationFlowBindingOverrides": {},
564
+ "fullScopeAllowed": false,
565
+ "nodeReRegistrationTimeout": 0,
566
+ "protocolMappers": [
567
+ {
568
+ "id": "c5c4ebe5-d009-4f96-b143-1b36d770eafb",
569
+ "name": "audience resolve",
570
+ "protocol": "openid-connect",
571
+ "protocolMapper": "oidc-audience-resolve-mapper",
572
+ "consentRequired": false,
573
+ "config": {}
574
+ }
575
+ ],
576
+ "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "email"],
577
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
578
+ },
579
+ {
580
+ "id": "bb166356-838d-445e-94e3-9330ad7ab51b",
581
+ "clientId": "admin-cli",
582
+ "name": "${client_admin-cli}",
583
+ "surrogateAuthRequired": false,
584
+ "enabled": true,
585
+ "alwaysDisplayInConsole": false,
586
+ "clientAuthenticatorType": "client-secret",
587
+ "secret": "1e154a7c-bc67-4106-b960-e1299fe649eb",
588
+ "redirectUris": [],
589
+ "webOrigins": [],
590
+ "notBefore": 0,
591
+ "bearerOnly": false,
592
+ "consentRequired": false,
593
+ "standardFlowEnabled": false,
594
+ "implicitFlowEnabled": false,
595
+ "directAccessGrantsEnabled": true,
596
+ "serviceAccountsEnabled": false,
597
+ "publicClient": true,
598
+ "frontchannelLogout": false,
599
+ "protocol": "openid-connect",
600
+ "attributes": {
601
+ "post.logout.redirect.uris": "+"
602
+ },
603
+ "authenticationFlowBindingOverrides": {},
604
+ "fullScopeAllowed": false,
605
+ "nodeReRegistrationTimeout": 0,
606
+ "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "email"],
607
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
608
+ },
609
+ {
610
+ "id": "88e1225b-f0b9-46ba-8efd-f2c10ce23058",
611
+ "clientId": "broker",
612
+ "name": "${client_broker}",
613
+ "surrogateAuthRequired": false,
614
+ "enabled": true,
615
+ "alwaysDisplayInConsole": false,
616
+ "clientAuthenticatorType": "client-secret",
617
+ "secret": "ab0f4144-c0da-4ccf-ba71-8b29761f8918",
618
+ "redirectUris": [],
619
+ "webOrigins": [],
620
+ "notBefore": 0,
621
+ "bearerOnly": false,
622
+ "consentRequired": false,
623
+ "standardFlowEnabled": true,
624
+ "implicitFlowEnabled": false,
625
+ "directAccessGrantsEnabled": false,
626
+ "serviceAccountsEnabled": false,
627
+ "publicClient": false,
628
+ "frontchannelLogout": false,
629
+ "protocol": "openid-connect",
630
+ "attributes": {
631
+ "post.logout.redirect.uris": "+"
632
+ },
633
+ "authenticationFlowBindingOverrides": {},
634
+ "fullScopeAllowed": false,
635
+ "nodeReRegistrationTimeout": 0,
636
+ "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "email"],
637
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
638
+ },
639
+ {
640
+ "id": "1acf7ad0-68cb-46a6-a3e4-8b2f2abecb85",
641
+ "clientId": "jhipster-control-center",
642
+ "rootUrl": "http://localhost:7419",
643
+ "adminUrl": "http://localhost:7419",
644
+ "surrogateAuthRequired": false,
645
+ "enabled": true,
646
+ "alwaysDisplayInConsole": false,
647
+ "clientAuthenticatorType": "client-secret",
648
+ "secret": "jhipster-control-center",
649
+ "redirectUris": ["dev.localhost.ionic:*", "http://127.0.0.1:*", "http://localhost:*", "https://127.0.0.1:*", "https://localhost:*"],
650
+ "webOrigins": ["*"],
651
+ "notBefore": 0,
652
+ "bearerOnly": false,
653
+ "consentRequired": false,
654
+ "standardFlowEnabled": true,
655
+ "implicitFlowEnabled": true,
656
+ "directAccessGrantsEnabled": false,
657
+ "serviceAccountsEnabled": false,
658
+ "publicClient": true,
659
+ "frontchannelLogout": false,
660
+ "protocol": "openid-connect",
661
+ "attributes": {
662
+ "saml.assertion.signature": "false",
663
+ "saml.force.post.binding": "false",
664
+ "saml.multivalued.roles": "false",
665
+ "saml.encrypt": "false",
666
+ "post.logout.redirect.uris": "+",
667
+ "saml.server.signature": "false",
668
+ "saml.server.signature.keyinfo.ext": "false",
669
+ "exclude.session.state.from.auth.response": "false",
670
+ "saml_force_name_id_format": "false",
671
+ "saml.client.signature": "false",
672
+ "tls.client.certificate.bound.access.tokens": "false",
673
+ "saml.authnstatement": "false",
674
+ "display.on.consent.screen": "false",
675
+ "saml.onetimeuse.condition": "false"
676
+ },
677
+ "authenticationFlowBindingOverrides": {},
678
+ "fullScopeAllowed": true,
679
+ "nodeReRegistrationTimeout": -1,
680
+ "defaultClientScopes": ["web-origins", "acr", "jhipster", "roles", "profile", "email"],
681
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
682
+ },
683
+ {
684
+ "id": "9057870f-8775-448d-a194-1d4e122f44d5",
685
+ "clientId": "jhipster-registry",
686
+ "rootUrl": "http://localhost:8761",
687
+ "adminUrl": "http://localhost:8761",
688
+ "surrogateAuthRequired": false,
689
+ "enabled": true,
690
+ "alwaysDisplayInConsole": false,
691
+ "clientAuthenticatorType": "client-secret",
692
+ "secret": "jhipster-registry",
693
+ "redirectUris": ["http://127.0.0.1:8761/*", "http://localhost:8761/*"],
694
+ "webOrigins": ["http://127.0.0.1:8761", "http://localhost:8761"],
695
+ "notBefore": 0,
696
+ "bearerOnly": false,
697
+ "consentRequired": false,
698
+ "standardFlowEnabled": true,
699
+ "implicitFlowEnabled": false,
700
+ "directAccessGrantsEnabled": true,
701
+ "serviceAccountsEnabled": false,
702
+ "publicClient": true,
703
+ "frontchannelLogout": false,
704
+ "protocol": "openid-connect",
705
+ "attributes": {
706
+ "saml.assertion.signature": "false",
707
+ "saml.force.post.binding": "false",
708
+ "saml.multivalued.roles": "false",
709
+ "saml.encrypt": "false",
710
+ "post.logout.redirect.uris": "+",
711
+ "saml.server.signature": "false",
712
+ "saml.server.signature.keyinfo.ext": "false",
713
+ "exclude.session.state.from.auth.response": "false",
714
+ "saml_force_name_id_format": "false",
715
+ "saml.client.signature": "false",
716
+ "tls.client.certificate.bound.access.tokens": "false",
717
+ "saml.authnstatement": "false",
718
+ "display.on.consent.screen": "false",
719
+ "saml.onetimeuse.condition": "false"
720
+ },
721
+ "authenticationFlowBindingOverrides": {},
722
+ "fullScopeAllowed": true,
723
+ "nodeReRegistrationTimeout": -1,
724
+ "defaultClientScopes": ["web-origins", "acr", "jhipster", "roles", "profile", "email"],
725
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
726
+ },
727
+ {
728
+ "id": "898488c8-e260-41c5-a463-7ceea14d587a",
729
+ "clientId": "realm-management",
730
+ "name": "${client_realm-management}",
731
+ "surrogateAuthRequired": false,
732
+ "enabled": true,
733
+ "alwaysDisplayInConsole": false,
734
+ "clientAuthenticatorType": "client-secret",
735
+ "secret": "fcc154e4-c747-4ea8-8433-7a34017c14a8",
736
+ "redirectUris": [],
737
+ "webOrigins": [],
738
+ "notBefore": 0,
739
+ "bearerOnly": true,
740
+ "consentRequired": false,
741
+ "standardFlowEnabled": true,
742
+ "implicitFlowEnabled": false,
743
+ "directAccessGrantsEnabled": false,
744
+ "serviceAccountsEnabled": false,
745
+ "publicClient": false,
746
+ "frontchannelLogout": false,
747
+ "protocol": "openid-connect",
748
+ "attributes": {
749
+ "post.logout.redirect.uris": "+"
750
+ },
751
+ "authenticationFlowBindingOverrides": {},
752
+ "fullScopeAllowed": false,
753
+ "nodeReRegistrationTimeout": 0,
754
+ "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "email"],
755
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
756
+ },
757
+ {
758
+ "id": "989d2b96-b820-4f9b-aa17-55e6488b08c8",
759
+ "clientId": "security-admin-console",
760
+ "name": "${client_security-admin-console}",
761
+ "rootUrl": "${authAdminUrl}",
762
+ "baseUrl": "/admin/jhipster/console/",
763
+ "surrogateAuthRequired": false,
764
+ "enabled": true,
765
+ "alwaysDisplayInConsole": false,
766
+ "clientAuthenticatorType": "client-secret",
767
+ "secret": "cf29935c-25d7-4029-bb7e-f6ebfa52f599",
768
+ "redirectUris": ["/admin/jhipster/console/*"],
769
+ "webOrigins": ["+"],
770
+ "notBefore": 0,
771
+ "bearerOnly": false,
772
+ "consentRequired": false,
773
+ "standardFlowEnabled": true,
774
+ "implicitFlowEnabled": false,
775
+ "directAccessGrantsEnabled": false,
776
+ "serviceAccountsEnabled": false,
777
+ "publicClient": true,
778
+ "frontchannelLogout": false,
779
+ "protocol": "openid-connect",
780
+ "attributes": {
781
+ "post.logout.redirect.uris": "+",
782
+ "pkce.code.challenge.method": "S256"
783
+ },
784
+ "authenticationFlowBindingOverrides": {},
785
+ "fullScopeAllowed": false,
786
+ "nodeReRegistrationTimeout": 0,
787
+ "protocolMappers": [
788
+ {
789
+ "id": "5fd34289-c644-411a-874c-849475d9d102",
790
+ "name": "locale",
791
+ "protocol": "openid-connect",
792
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
793
+ "consentRequired": false,
794
+ "config": {
795
+ "userinfo.token.claim": "true",
796
+ "user.attribute": "locale",
797
+ "id.token.claim": "true",
798
+ "access.token.claim": "true",
799
+ "claim.name": "locale",
800
+ "jsonType.label": "String"
801
+ }
802
+ }
803
+ ],
804
+ "defaultClientScopes": ["web-origins", "acr", "roles", "profile", "email"],
805
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
806
+ },
807
+ {
808
+ "id": "6e8deddb-b4d6-4e2e-b389-b397d3f74fcd",
809
+ "clientId": "web_app",
810
+ "rootUrl": "http://localhost:8081",
811
+ "adminUrl": "http://localhost:8081",
812
+ "surrogateAuthRequired": false,
813
+ "enabled": true,
814
+ "alwaysDisplayInConsole": false,
815
+ "clientAuthenticatorType": "client-secret",
816
+ "secret": "web_app",
817
+ "redirectUris": [
818
+ "dev.localhost.ionic:*",
819
+ "http://127.0.0.1:*",
820
+ "http://localhost:*",
821
+ "https://127.0.0.1:*",
822
+ "https://localhost:*",
823
+ "https://oauth.pstmn.io/v1/callback"
824
+ ],
825
+ "webOrigins": ["*"],
826
+ "notBefore": 0,
827
+ "bearerOnly": false,
828
+ "consentRequired": false,
829
+ "standardFlowEnabled": true,
830
+ "implicitFlowEnabled": true,
831
+ "directAccessGrantsEnabled": false,
832
+ "serviceAccountsEnabled": false,
833
+ "publicClient": true,
834
+ "frontchannelLogout": false,
835
+ "protocol": "openid-connect",
836
+ "attributes": {
837
+ "saml.assertion.signature": "false",
838
+ "saml.force.post.binding": "false",
839
+ "saml.multivalued.roles": "false",
840
+ "saml.encrypt": "false",
841
+ "post.logout.redirect.uris": "+",
842
+ "saml.server.signature": "false",
843
+ "saml.server.signature.keyinfo.ext": "false",
844
+ "exclude.session.state.from.auth.response": "false",
845
+ "saml_force_name_id_format": "false",
846
+ "saml.client.signature": "false",
847
+ "tls.client.certificate.bound.access.tokens": "false",
848
+ "saml.authnstatement": "false",
849
+ "display.on.consent.screen": "false",
850
+ "saml.onetimeuse.condition": "false"
851
+ },
852
+ "authenticationFlowBindingOverrides": {},
853
+ "fullScopeAllowed": true,
854
+ "nodeReRegistrationTimeout": -1,
855
+ "defaultClientScopes": ["web-origins", "acr", "jhipster", "roles", "profile", "email"],
856
+ "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"]
857
+ }
858
+ ],
859
+ "clientScopes": [
860
+ {
861
+ "id": "52d73c82-423c-44a8-b2ec-1e13f4cd6065",
862
+ "name": "address",
863
+ "description": "OpenID Connect built-in scope: address",
864
+ "protocol": "openid-connect",
865
+ "attributes": {
866
+ "include.in.token.scope": "true",
867
+ "display.on.consent.screen": "true",
868
+ "consent.screen.text": "${addressScopeConsentText}"
869
+ },
870
+ "protocolMappers": [
871
+ {
872
+ "id": "98230752-36b9-4755-8661-a7de1926d0d4",
873
+ "name": "address",
874
+ "protocol": "openid-connect",
875
+ "protocolMapper": "oidc-address-mapper",
876
+ "consentRequired": false,
877
+ "config": {
878
+ "user.attribute.formatted": "formatted",
879
+ "user.attribute.country": "country",
880
+ "user.attribute.postal_code": "postal_code",
881
+ "userinfo.token.claim": "true",
882
+ "user.attribute.street": "street",
883
+ "id.token.claim": "true",
884
+ "user.attribute.region": "region",
885
+ "access.token.claim": "true",
886
+ "user.attribute.locality": "locality"
887
+ }
888
+ }
889
+ ]
890
+ },
891
+ {
892
+ "id": "44d24405-87bf-4b37-a627-e3fdabb93f50",
893
+ "name": "email",
894
+ "description": "OpenID Connect built-in scope: email",
895
+ "protocol": "openid-connect",
896
+ "attributes": {
897
+ "include.in.token.scope": "true",
898
+ "display.on.consent.screen": "true",
899
+ "consent.screen.text": "${emailScopeConsentText}"
900
+ },
901
+ "protocolMappers": [
902
+ {
903
+ "id": "36800088-6d17-4c18-93e8-2bc93901d8b7",
904
+ "name": "email",
905
+ "protocol": "openid-connect",
906
+ "protocolMapper": "oidc-usermodel-property-mapper",
907
+ "consentRequired": false,
908
+ "config": {
909
+ "userinfo.token.claim": "true",
910
+ "user.attribute": "email",
911
+ "id.token.claim": "true",
912
+ "access.token.claim": "true",
913
+ "claim.name": "email",
914
+ "jsonType.label": "String"
915
+ }
916
+ },
917
+ {
918
+ "id": "3ea34afd-30b5-4e5d-a836-dbda439dce6f",
919
+ "name": "email verified",
920
+ "protocol": "openid-connect",
921
+ "protocolMapper": "oidc-usermodel-property-mapper",
922
+ "consentRequired": false,
923
+ "config": {
924
+ "userinfo.token.claim": "true",
925
+ "user.attribute": "emailVerified",
926
+ "id.token.claim": "true",
927
+ "access.token.claim": "true",
928
+ "claim.name": "email_verified",
929
+ "jsonType.label": "boolean"
930
+ }
931
+ }
932
+ ]
933
+ },
934
+ {
935
+ "id": "9816de82-24b7-42fe-a85a-1264868ec293",
936
+ "name": "jhipster",
937
+ "description": "Jhipster specific claims",
938
+ "protocol": "openid-connect",
939
+ "attributes": {
940
+ "include.in.token.scope": "false",
941
+ "display.on.consent.screen": "false"
942
+ },
943
+ "protocolMappers": [
944
+ {
945
+ "id": "0f9c9347-aad6-4bff-94f4-e11937f2ad33",
946
+ "name": "langKey",
947
+ "protocol": "openid-connect",
948
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
949
+ "consentRequired": false,
950
+ "config": {
951
+ "userinfo.token.claim": "true",
952
+ "user.attribute": "langKey",
953
+ "id.token.claim": "false",
954
+ "access.token.claim": "false",
955
+ "claim.name": "langKey",
956
+ "jsonType.label": "String"
957
+ }
958
+ },
959
+ {
960
+ "id": "69729907-8d1c-4961-81c0-91766f548cc9",
961
+ "name": "roles",
962
+ "protocol": "openid-connect",
963
+ "protocolMapper": "oidc-usermodel-realm-role-mapper",
964
+ "consentRequired": false,
965
+ "config": {
966
+ "multivalued": "true",
967
+ "userinfo.token.claim": "true",
968
+ "id.token.claim": "false",
969
+ "access.token.claim": "true",
970
+ "claim.name": "roles",
971
+ "jsonType.label": "String"
972
+ }
973
+ },
974
+ {
975
+ "id": "336acfe2-a717-492a-9055-5b70e808f42f",
976
+ "name": "login",
977
+ "protocol": "openid-connect",
978
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
979
+ "consentRequired": false,
980
+ "config": {
981
+ "userinfo.token.claim": "true",
982
+ "user.attribute": "preferred_username",
983
+ "id.token.claim": "false",
984
+ "access.token.claim": "false",
985
+ "claim.name": "login",
986
+ "jsonType.label": "String"
987
+ }
988
+ }
989
+ ]
990
+ },
991
+ {
992
+ "id": "87d299f2-434f-4abd-8cb0-a16231acd713",
993
+ "name": "microprofile-jwt",
994
+ "description": "Microprofile - JWT built-in scope",
995
+ "protocol": "openid-connect",
996
+ "attributes": {
997
+ "include.in.token.scope": "true",
998
+ "display.on.consent.screen": "false"
999
+ },
1000
+ "protocolMappers": [
1001
+ {
1002
+ "id": "fce09d51-cb85-4ccd-b83d-865a4d4bf650",
1003
+ "name": "groups",
1004
+ "protocol": "openid-connect",
1005
+ "protocolMapper": "oidc-usermodel-realm-role-mapper",
1006
+ "consentRequired": false,
1007
+ "config": {
1008
+ "multivalued": "true",
1009
+ "userinfo.token.claim": "true",
1010
+ "user.attribute": "foo",
1011
+ "id.token.claim": "true",
1012
+ "access.token.claim": "true",
1013
+ "claim.name": "groups",
1014
+ "jsonType.label": "String"
1015
+ }
1016
+ },
1017
+ {
1018
+ "id": "3d1ee7e2-b7e1-4504-bd52-b47a2cb10eec",
1019
+ "name": "upn",
1020
+ "protocol": "openid-connect",
1021
+ "protocolMapper": "oidc-usermodel-property-mapper",
1022
+ "consentRequired": false,
1023
+ "config": {
1024
+ "userinfo.token.claim": "true",
1025
+ "user.attribute": "username",
1026
+ "id.token.claim": "true",
1027
+ "access.token.claim": "true",
1028
+ "claim.name": "upn",
1029
+ "jsonType.label": "String"
1030
+ }
1031
+ }
1032
+ ]
1033
+ },
1034
+ {
1035
+ "id": "0399b625-22d7-4d68-b4db-fd1dc2effacc",
1036
+ "name": "offline_access",
1037
+ "description": "OpenID Connect built-in scope: offline_access",
1038
+ "protocol": "openid-connect",
1039
+ "attributes": {
1040
+ "consent.screen.text": "${offlineAccessScopeConsentText}",
1041
+ "display.on.consent.screen": "true"
1042
+ }
1043
+ },
1044
+ {
1045
+ "id": "2b867b2d-3373-43ff-b50f-ea37a5e1c390",
1046
+ "name": "phone",
1047
+ "description": "OpenID Connect built-in scope: phone",
1048
+ "protocol": "openid-connect",
1049
+ "attributes": {
1050
+ "include.in.token.scope": "true",
1051
+ "display.on.consent.screen": "true",
1052
+ "consent.screen.text": "${phoneScopeConsentText}"
1053
+ },
1054
+ "protocolMappers": [
1055
+ {
1056
+ "id": "daa0191b-20d1-4f71-b191-6c48a37e3677",
1057
+ "name": "phone number",
1058
+ "protocol": "openid-connect",
1059
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1060
+ "consentRequired": false,
1061
+ "config": {
1062
+ "userinfo.token.claim": "true",
1063
+ "user.attribute": "phoneNumber",
1064
+ "id.token.claim": "true",
1065
+ "access.token.claim": "true",
1066
+ "claim.name": "phone_number",
1067
+ "jsonType.label": "String"
1068
+ }
1069
+ },
1070
+ {
1071
+ "id": "32213de7-12f7-4864-b696-c8e6c5e0c26e",
1072
+ "name": "phone number verified",
1073
+ "protocol": "openid-connect",
1074
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1075
+ "consentRequired": false,
1076
+ "config": {
1077
+ "userinfo.token.claim": "true",
1078
+ "user.attribute": "phoneNumberVerified",
1079
+ "id.token.claim": "true",
1080
+ "access.token.claim": "true",
1081
+ "claim.name": "phone_number_verified",
1082
+ "jsonType.label": "boolean"
1083
+ }
1084
+ }
1085
+ ]
1086
+ },
1087
+ {
1088
+ "id": "60a44832-9776-449f-94cd-fa8c24a75f35",
1089
+ "name": "profile",
1090
+ "description": "OpenID Connect built-in scope: profile",
1091
+ "protocol": "openid-connect",
1092
+ "attributes": {
1093
+ "include.in.token.scope": "true",
1094
+ "display.on.consent.screen": "true",
1095
+ "consent.screen.text": "${profileScopeConsentText}"
1096
+ },
1097
+ "protocolMappers": [
1098
+ {
1099
+ "id": "a59584ab-7a7c-4b23-95b5-be8dbbfadc6f",
1100
+ "name": "family name",
1101
+ "protocol": "openid-connect",
1102
+ "protocolMapper": "oidc-usermodel-property-mapper",
1103
+ "consentRequired": false,
1104
+ "config": {
1105
+ "userinfo.token.claim": "true",
1106
+ "user.attribute": "lastName",
1107
+ "id.token.claim": "true",
1108
+ "access.token.claim": "true",
1109
+ "claim.name": "family_name",
1110
+ "jsonType.label": "String"
1111
+ }
1112
+ },
1113
+ {
1114
+ "id": "d382c1dc-d5d8-479e-8809-f0a618113a07",
1115
+ "name": "website",
1116
+ "protocol": "openid-connect",
1117
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1118
+ "consentRequired": false,
1119
+ "config": {
1120
+ "userinfo.token.claim": "true",
1121
+ "user.attribute": "website",
1122
+ "id.token.claim": "true",
1123
+ "access.token.claim": "true",
1124
+ "claim.name": "website",
1125
+ "jsonType.label": "String"
1126
+ }
1127
+ },
1128
+ {
1129
+ "id": "559f86c1-1187-498d-8354-723f4ea5721c",
1130
+ "name": "full name",
1131
+ "protocol": "openid-connect",
1132
+ "protocolMapper": "oidc-full-name-mapper",
1133
+ "consentRequired": false,
1134
+ "config": {
1135
+ "id.token.claim": "true",
1136
+ "access.token.claim": "true",
1137
+ "userinfo.token.claim": "true"
1138
+ }
1139
+ },
1140
+ {
1141
+ "id": "0925e106-a8e2-4ad1-b75e-4147d185894a",
1142
+ "name": "updated at",
1143
+ "protocol": "openid-connect",
1144
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1145
+ "consentRequired": false,
1146
+ "config": {
1147
+ "userinfo.token.claim": "true",
1148
+ "user.attribute": "updatedAt",
1149
+ "id.token.claim": "true",
1150
+ "access.token.claim": "true",
1151
+ "claim.name": "updated_at",
1152
+ "jsonType.label": "String"
1153
+ }
1154
+ },
1155
+ {
1156
+ "id": "eb8e2c73-5c65-4b53-8d55-46edef61315b",
1157
+ "name": "locale",
1158
+ "protocol": "openid-connect",
1159
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1160
+ "consentRequired": false,
1161
+ "config": {
1162
+ "userinfo.token.claim": "true",
1163
+ "user.attribute": "locale",
1164
+ "id.token.claim": "true",
1165
+ "access.token.claim": "true",
1166
+ "claim.name": "locale",
1167
+ "jsonType.label": "String"
1168
+ }
1169
+ },
1170
+ {
1171
+ "id": "4c109376-01bc-4b69-a3c0-4b830ecad674",
1172
+ "name": "middle name",
1173
+ "protocol": "openid-connect",
1174
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1175
+ "consentRequired": false,
1176
+ "config": {
1177
+ "userinfo.token.claim": "true",
1178
+ "user.attribute": "middleName",
1179
+ "id.token.claim": "true",
1180
+ "access.token.claim": "true",
1181
+ "claim.name": "middle_name",
1182
+ "jsonType.label": "String"
1183
+ }
1184
+ },
1185
+ {
1186
+ "id": "b3813956-e556-4b57-a06b-f71b0d6f3d47",
1187
+ "name": "nickname",
1188
+ "protocol": "openid-connect",
1189
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1190
+ "consentRequired": false,
1191
+ "config": {
1192
+ "userinfo.token.claim": "true",
1193
+ "user.attribute": "nickname",
1194
+ "id.token.claim": "true",
1195
+ "access.token.claim": "true",
1196
+ "claim.name": "nickname",
1197
+ "jsonType.label": "String"
1198
+ }
1199
+ },
1200
+ {
1201
+ "id": "28beb4c0-029b-4aa5-ad5f-6d824ca67e15",
1202
+ "name": "username",
1203
+ "protocol": "openid-connect",
1204
+ "protocolMapper": "oidc-usermodel-property-mapper",
1205
+ "consentRequired": false,
1206
+ "config": {
1207
+ "userinfo.token.claim": "true",
1208
+ "user.attribute": "username",
1209
+ "id.token.claim": "true",
1210
+ "access.token.claim": "true",
1211
+ "claim.name": "preferred_username",
1212
+ "jsonType.label": "String"
1213
+ }
1214
+ },
1215
+ {
1216
+ "id": "53d681bc-ec29-4f57-924b-ff5bd22d4093",
1217
+ "name": "profile",
1218
+ "protocol": "openid-connect",
1219
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1220
+ "consentRequired": false,
1221
+ "config": {
1222
+ "userinfo.token.claim": "true",
1223
+ "user.attribute": "profile",
1224
+ "id.token.claim": "true",
1225
+ "access.token.claim": "true",
1226
+ "claim.name": "profile",
1227
+ "jsonType.label": "String"
1228
+ }
1229
+ },
1230
+ {
1231
+ "id": "12ba8e12-157d-4729-918b-0d74fa444fba",
1232
+ "name": "picture",
1233
+ "protocol": "openid-connect",
1234
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1235
+ "consentRequired": false,
1236
+ "config": {
1237
+ "userinfo.token.claim": "true",
1238
+ "user.attribute": "picture",
1239
+ "id.token.claim": "true",
1240
+ "access.token.claim": "true",
1241
+ "claim.name": "picture",
1242
+ "jsonType.label": "String"
1243
+ }
1244
+ },
1245
+ {
1246
+ "id": "ddb818fe-8e4a-4b26-9c5d-2467a26af6dc",
1247
+ "name": "gender",
1248
+ "protocol": "openid-connect",
1249
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1250
+ "consentRequired": false,
1251
+ "config": {
1252
+ "userinfo.token.claim": "true",
1253
+ "user.attribute": "gender",
1254
+ "id.token.claim": "true",
1255
+ "access.token.claim": "true",
1256
+ "claim.name": "gender",
1257
+ "jsonType.label": "String"
1258
+ }
1259
+ },
1260
+ {
1261
+ "id": "f78b1746-2be1-45f4-9c1e-1f6141ccdb65",
1262
+ "name": "birthdate",
1263
+ "protocol": "openid-connect",
1264
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1265
+ "consentRequired": false,
1266
+ "config": {
1267
+ "userinfo.token.claim": "true",
1268
+ "user.attribute": "birthdate",
1269
+ "id.token.claim": "true",
1270
+ "access.token.claim": "true",
1271
+ "claim.name": "birthdate",
1272
+ "jsonType.label": "String"
1273
+ }
1274
+ },
1275
+ {
1276
+ "id": "7723245c-4952-4822-86ae-084048b1f2f2",
1277
+ "name": "given name",
1278
+ "protocol": "openid-connect",
1279
+ "protocolMapper": "oidc-usermodel-property-mapper",
1280
+ "consentRequired": false,
1281
+ "config": {
1282
+ "userinfo.token.claim": "true",
1283
+ "user.attribute": "firstName",
1284
+ "id.token.claim": "true",
1285
+ "access.token.claim": "true",
1286
+ "claim.name": "given_name",
1287
+ "jsonType.label": "String"
1288
+ }
1289
+ },
1290
+ {
1291
+ "id": "b192fe9f-aa82-4d7d-b8c7-eb7d1ba888d4",
1292
+ "name": "zoneinfo",
1293
+ "protocol": "openid-connect",
1294
+ "protocolMapper": "oidc-usermodel-attribute-mapper",
1295
+ "consentRequired": false,
1296
+ "config": {
1297
+ "userinfo.token.claim": "true",
1298
+ "user.attribute": "zoneinfo",
1299
+ "id.token.claim": "true",
1300
+ "access.token.claim": "true",
1301
+ "claim.name": "zoneinfo",
1302
+ "jsonType.label": "String"
1303
+ }
1304
+ }
1305
+ ]
1306
+ },
1307
+ {
1308
+ "id": "d181691e-b4a6-4063-9eba-6b984402a9a7",
1309
+ "name": "role_list",
1310
+ "description": "SAML role list",
1311
+ "protocol": "saml",
1312
+ "attributes": {
1313
+ "consent.screen.text": "${samlRoleListScopeConsentText}",
1314
+ "display.on.consent.screen": "true"
1315
+ },
1316
+ "protocolMappers": [
1317
+ {
1318
+ "id": "724b16d4-8a9b-42d8-850f-99ca1ab3c958",
1319
+ "name": "role list",
1320
+ "protocol": "saml",
1321
+ "protocolMapper": "saml-role-list-mapper",
1322
+ "consentRequired": false,
1323
+ "config": {
1324
+ "single": "false",
1325
+ "attribute.nameformat": "Basic",
1326
+ "attribute.name": "Role"
1327
+ }
1328
+ }
1329
+ ]
1330
+ },
1331
+ {
1332
+ "id": "915fcb95-81da-4e4c-86ee-73f3b52c83e9",
1333
+ "name": "roles",
1334
+ "description": "OpenID Connect scope for add user roles to the access token",
1335
+ "protocol": "openid-connect",
1336
+ "attributes": {
1337
+ "include.in.token.scope": "false",
1338
+ "display.on.consent.screen": "true",
1339
+ "consent.screen.text": "${rolesScopeConsentText}"
1340
+ },
1341
+ "protocolMappers": [
1342
+ {
1343
+ "id": "12f0b32d-8911-4028-809b-fc1c0e5e9207",
1344
+ "name": "audience resolve",
1345
+ "protocol": "openid-connect",
1346
+ "protocolMapper": "oidc-audience-resolve-mapper",
1347
+ "consentRequired": false,
1348
+ "config": {}
1349
+ },
1350
+ {
1351
+ "id": "5b997b66-937f-46d3-9e8b-70dca949f682",
1352
+ "name": "realm roles",
1353
+ "protocol": "openid-connect",
1354
+ "protocolMapper": "oidc-usermodel-realm-role-mapper",
1355
+ "consentRequired": false,
1356
+ "config": {
1357
+ "user.attribute": "foo",
1358
+ "access.token.claim": "true",
1359
+ "claim.name": "realm_access.roles",
1360
+ "jsonType.label": "String",
1361
+ "multivalued": "true"
1362
+ }
1363
+ },
1364
+ {
1365
+ "id": "cdcd6969-a9aa-4de5-adbe-dc83da4184c5",
1366
+ "name": "client roles",
1367
+ "protocol": "openid-connect",
1368
+ "protocolMapper": "oidc-usermodel-client-role-mapper",
1369
+ "consentRequired": false,
1370
+ "config": {
1371
+ "user.attribute": "foo",
1372
+ "access.token.claim": "true",
1373
+ "claim.name": "resource_access.${client_id}.roles",
1374
+ "jsonType.label": "String",
1375
+ "multivalued": "true"
1376
+ }
1377
+ }
1378
+ ]
1379
+ },
1380
+ {
1381
+ "id": "49177925-3cb4-4fe1-9ced-d9a331dee5c6",
1382
+ "name": "acr",
1383
+ "description": "OpenID Connect scope for add acr (authentication context class reference) to the token",
1384
+ "protocol": "openid-connect",
1385
+ "attributes": {
1386
+ "include.in.token.scope": "false",
1387
+ "display.on.consent.screen": "false"
1388
+ },
1389
+ "protocolMappers": [
1390
+ {
1391
+ "id": "df11a217-a90d-4d01-9aab-84fbaa3a0ad6",
1392
+ "name": "acr loa level",
1393
+ "protocol": "openid-connect",
1394
+ "protocolMapper": "oidc-acr-mapper",
1395
+ "consentRequired": false,
1396
+ "config": {
1397
+ "id.token.claim": "true",
1398
+ "access.token.claim": "true"
1399
+ }
1400
+ }
1401
+ ]
1402
+ },
1403
+ {
1404
+ "id": "2daaac74-636f-4074-87a9-d1aba9dffb96",
1405
+ "name": "web-origins",
1406
+ "description": "OpenID Connect scope for add allowed web origins to the access token",
1407
+ "protocol": "openid-connect",
1408
+ "attributes": {
1409
+ "include.in.token.scope": "false",
1410
+ "display.on.consent.screen": "false",
1411
+ "consent.screen.text": ""
1412
+ },
1413
+ "protocolMappers": [
1414
+ {
1415
+ "id": "752e035f-038d-46ac-b65d-91f863fdd986",
1416
+ "name": "allowed web origins",
1417
+ "protocol": "openid-connect",
1418
+ "protocolMapper": "oidc-allowed-origins-mapper",
1419
+ "consentRequired": false,
1420
+ "config": {}
1421
+ }
1422
+ ]
1423
+ }
1424
+ ],
1425
+ "defaultDefaultClientScopes": ["web-origins", "acr", "email", "profile", "roles", "role_list"],
1426
+ "defaultOptionalClientScopes": ["offline_access", "phone", "address", "microprofile-jwt"],
1427
+ "browserSecurityHeaders": {
1428
+ "contentSecurityPolicyReportOnly": "",
1429
+ "xContentTypeOptions": "nosniff",
1430
+ "xRobotsTag": "none",
1431
+ "xFrameOptions": "SAMEORIGIN",
1432
+ "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';",
1433
+ "xXSSProtection": "1; mode=block",
1434
+ "strictTransportSecurity": "max-age=31536000; includeSubDomains"
1435
+ },
1436
+ "smtpServer": {},
1437
+ "eventsEnabled": false,
1438
+ "eventsListeners": ["jboss-logging"],
1439
+ "enabledEventTypes": [],
1440
+ "adminEventsEnabled": false,
1441
+ "adminEventsDetailsEnabled": false,
1442
+ "identityProviders": [],
1443
+ "identityProviderMappers": [],
1444
+ "components": {
1445
+ "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [
1446
+ {
1447
+ "id": "827fde01-dc1b-4c1f-a529-9ef833ca3432",
1448
+ "name": "Allowed Protocol Mapper Types",
1449
+ "providerId": "allowed-protocol-mappers",
1450
+ "subType": "authenticated",
1451
+ "subComponents": {},
1452
+ "config": {
1453
+ "allowed-protocol-mapper-types": [
1454
+ "oidc-usermodel-property-mapper",
1455
+ "oidc-sha256-pairwise-sub-mapper",
1456
+ "saml-user-attribute-mapper",
1457
+ "saml-user-property-mapper",
1458
+ "oidc-address-mapper",
1459
+ "oidc-full-name-mapper",
1460
+ "saml-role-list-mapper",
1461
+ "oidc-usermodel-attribute-mapper"
1462
+ ]
1463
+ }
1464
+ },
1465
+ {
1466
+ "id": "0a429e7e-be7a-46b4-b42a-d1f8b265ff16",
1467
+ "name": "Allowed Client Scopes",
1468
+ "providerId": "allowed-client-templates",
1469
+ "subType": "authenticated",
1470
+ "subComponents": {},
1471
+ "config": {
1472
+ "allow-default-scopes": ["true"]
1473
+ }
1474
+ },
1475
+ {
1476
+ "id": "5a1ff0b4-250f-48ee-8169-abff30cf7534",
1477
+ "name": "Allowed Client Scopes",
1478
+ "providerId": "allowed-client-templates",
1479
+ "subType": "anonymous",
1480
+ "subComponents": {},
1481
+ "config": {
1482
+ "allow-default-scopes": ["true"]
1483
+ }
1484
+ },
1485
+ {
1486
+ "id": "c79f6629-84a9-467c-81d0-63e20b19f916",
1487
+ "name": "Full Scope Disabled",
1488
+ "providerId": "scope",
1489
+ "subType": "anonymous",
1490
+ "subComponents": {},
1491
+ "config": {}
1492
+ },
1493
+ {
1494
+ "id": "b6b23ef8-96e8-4e2e-8efe-8003057a8d42",
1495
+ "name": "Max Clients Limit",
1496
+ "providerId": "max-clients",
1497
+ "subType": "anonymous",
1498
+ "subComponents": {},
1499
+ "config": {
1500
+ "max-clients": ["200"]
1501
+ }
1502
+ },
1503
+ {
1504
+ "id": "36dfaa02-0252-4448-9cdf-a17abf239f78",
1505
+ "name": "Trusted Hosts",
1506
+ "providerId": "trusted-hosts",
1507
+ "subType": "anonymous",
1508
+ "subComponents": {},
1509
+ "config": {
1510
+ "host-sending-registration-request-must-match": ["true"],
1511
+ "client-uris-must-match": ["true"]
1512
+ }
1513
+ },
1514
+ {
1515
+ "id": "8216421d-34fb-4726-8331-137217657bdb",
1516
+ "name": "Allowed Protocol Mapper Types",
1517
+ "providerId": "allowed-protocol-mappers",
1518
+ "subType": "anonymous",
1519
+ "subComponents": {},
1520
+ "config": {
1521
+ "allowed-protocol-mapper-types": [
1522
+ "oidc-address-mapper",
1523
+ "oidc-usermodel-attribute-mapper",
1524
+ "saml-user-property-mapper",
1525
+ "saml-user-attribute-mapper",
1526
+ "saml-role-list-mapper",
1527
+ "oidc-full-name-mapper",
1528
+ "oidc-usermodel-property-mapper",
1529
+ "oidc-sha256-pairwise-sub-mapper"
1530
+ ]
1531
+ }
1532
+ },
1533
+ {
1534
+ "id": "d045f3f9-15e6-4e69-a419-0e7ff8a635ef",
1535
+ "name": "Consent Required",
1536
+ "providerId": "consent-required",
1537
+ "subType": "anonymous",
1538
+ "subComponents": {},
1539
+ "config": {}
1540
+ }
1541
+ ],
1542
+ "org.keycloak.userprofile.UserProfileProvider": [
1543
+ {
1544
+ "id": "b05ccf0d-d8ac-4695-bd60-37018f8f94b4",
1545
+ "providerId": "declarative-user-profile",
1546
+ "subComponents": {},
1547
+ "config": {}
1548
+ }
1549
+ ],
1550
+ "org.keycloak.keys.KeyProvider": [
1551
+ {
1552
+ "id": "62707fae-58f9-4fc2-89fb-0c5d212dc3dc",
1553
+ "name": "rsa-generated",
1554
+ "providerId": "rsa-generated",
1555
+ "subComponents": {},
1556
+ "config": {
1557
+ "priority": ["100"]
1558
+ }
1559
+ },
1560
+ {
1561
+ "id": "4a8480bc-96fd-4906-a907-f948a73bab38",
1562
+ "name": "hmac-generated",
1563
+ "providerId": "hmac-generated",
1564
+ "subComponents": {},
1565
+ "config": {
1566
+ "priority": ["100"],
1567
+ "algorithm": ["HS256"]
1568
+ }
1569
+ },
1570
+ {
1571
+ "id": "40c01a32-0c0b-4dbb-9595-e5a5c8d26bc4",
1572
+ "name": "aes-generated",
1573
+ "providerId": "aes-generated",
1574
+ "subComponents": {},
1575
+ "config": {
1576
+ "priority": ["100"]
1577
+ }
1578
+ }
1579
+ ]
1580
+ },
1581
+ "internationalizationEnabled": false,
1582
+ "supportedLocales": [],
1583
+ "authenticationFlows": [
1584
+ {
1585
+ "id": "491fbbc9-b70b-45bd-8243-2039ae3f115d",
1586
+ "alias": "Account verification options",
1587
+ "description": "Method with which to verity the existing account",
1588
+ "providerId": "basic-flow",
1589
+ "topLevel": false,
1590
+ "builtIn": true,
1591
+ "authenticationExecutions": [
1592
+ {
1593
+ "authenticator": "idp-email-verification",
1594
+ "authenticatorFlow": false,
1595
+ "requirement": "ALTERNATIVE",
1596
+ "priority": 10,
1597
+ "userSetupAllowed": false,
1598
+ "autheticatorFlow": false
1599
+ },
1600
+ {
1601
+ "authenticatorFlow": true,
1602
+ "requirement": "ALTERNATIVE",
1603
+ "priority": 20,
1604
+ "flowAlias": "Verify Existing Account by Re-authentication",
1605
+ "userSetupAllowed": false,
1606
+ "autheticatorFlow": true
1607
+ }
1608
+ ]
1609
+ },
1610
+ {
1611
+ "id": "2c63ad60-76ab-4350-9def-74328bab70d0",
1612
+ "alias": "Authentication Options",
1613
+ "description": "Authentication options.",
1614
+ "providerId": "basic-flow",
1615
+ "topLevel": false,
1616
+ "builtIn": true,
1617
+ "authenticationExecutions": [
1618
+ {
1619
+ "authenticator": "basic-auth",
1620
+ "authenticatorFlow": false,
1621
+ "requirement": "REQUIRED",
1622
+ "priority": 10,
1623
+ "userSetupAllowed": false,
1624
+ "autheticatorFlow": false
1625
+ },
1626
+ {
1627
+ "authenticator": "basic-auth-otp",
1628
+ "authenticatorFlow": false,
1629
+ "requirement": "DISABLED",
1630
+ "priority": 20,
1631
+ "userSetupAllowed": false,
1632
+ "autheticatorFlow": false
1633
+ },
1634
+ {
1635
+ "authenticator": "auth-spnego",
1636
+ "authenticatorFlow": false,
1637
+ "requirement": "DISABLED",
1638
+ "priority": 30,
1639
+ "userSetupAllowed": false,
1640
+ "autheticatorFlow": false
1641
+ }
1642
+ ]
1643
+ },
1644
+ {
1645
+ "id": "82b9b584-2243-4893-b58c-4567f34434a6",
1646
+ "alias": "Browser - Conditional OTP",
1647
+ "description": "Flow to determine if the OTP is required for the authentication",
1648
+ "providerId": "basic-flow",
1649
+ "topLevel": false,
1650
+ "builtIn": true,
1651
+ "authenticationExecutions": [
1652
+ {
1653
+ "authenticator": "conditional-user-configured",
1654
+ "authenticatorFlow": false,
1655
+ "requirement": "REQUIRED",
1656
+ "priority": 10,
1657
+ "userSetupAllowed": false,
1658
+ "autheticatorFlow": false
1659
+ },
1660
+ {
1661
+ "authenticator": "auth-otp-form",
1662
+ "authenticatorFlow": false,
1663
+ "requirement": "REQUIRED",
1664
+ "priority": 20,
1665
+ "userSetupAllowed": false,
1666
+ "autheticatorFlow": false
1667
+ }
1668
+ ]
1669
+ },
1670
+ {
1671
+ "id": "e70e7c74-8ab5-411c-b06c-d478a452bee3",
1672
+ "alias": "Direct Grant - Conditional OTP",
1673
+ "description": "Flow to determine if the OTP is required for the authentication",
1674
+ "providerId": "basic-flow",
1675
+ "topLevel": false,
1676
+ "builtIn": true,
1677
+ "authenticationExecutions": [
1678
+ {
1679
+ "authenticator": "conditional-user-configured",
1680
+ "authenticatorFlow": false,
1681
+ "requirement": "REQUIRED",
1682
+ "priority": 10,
1683
+ "userSetupAllowed": false,
1684
+ "autheticatorFlow": false
1685
+ },
1686
+ {
1687
+ "authenticator": "direct-grant-validate-otp",
1688
+ "authenticatorFlow": false,
1689
+ "requirement": "REQUIRED",
1690
+ "priority": 20,
1691
+ "userSetupAllowed": false,
1692
+ "autheticatorFlow": false
1693
+ }
1694
+ ]
1695
+ },
1696
+ {
1697
+ "id": "4f3e6fdd-9b4d-4dc0-946a-1e1ccae7af71",
1698
+ "alias": "First broker login - Conditional OTP",
1699
+ "description": "Flow to determine if the OTP is required for the authentication",
1700
+ "providerId": "basic-flow",
1701
+ "topLevel": false,
1702
+ "builtIn": true,
1703
+ "authenticationExecutions": [
1704
+ {
1705
+ "authenticator": "conditional-user-configured",
1706
+ "authenticatorFlow": false,
1707
+ "requirement": "REQUIRED",
1708
+ "priority": 10,
1709
+ "userSetupAllowed": false,
1710
+ "autheticatorFlow": false
1711
+ },
1712
+ {
1713
+ "authenticator": "auth-otp-form",
1714
+ "authenticatorFlow": false,
1715
+ "requirement": "REQUIRED",
1716
+ "priority": 20,
1717
+ "userSetupAllowed": false,
1718
+ "autheticatorFlow": false
1719
+ }
1720
+ ]
1721
+ },
1722
+ {
1723
+ "id": "aa66c794-f21b-4663-9de1-9e27a7e425ab",
1724
+ "alias": "Handle Existing Account",
1725
+ "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider",
1726
+ "providerId": "basic-flow",
1727
+ "topLevel": false,
1728
+ "builtIn": true,
1729
+ "authenticationExecutions": [
1730
+ {
1731
+ "authenticator": "idp-confirm-link",
1732
+ "authenticatorFlow": false,
1733
+ "requirement": "REQUIRED",
1734
+ "priority": 10,
1735
+ "userSetupAllowed": false,
1736
+ "autheticatorFlow": false
1737
+ },
1738
+ {
1739
+ "authenticatorFlow": true,
1740
+ "requirement": "REQUIRED",
1741
+ "priority": 20,
1742
+ "flowAlias": "Account verification options",
1743
+ "userSetupAllowed": false,
1744
+ "autheticatorFlow": true
1745
+ }
1746
+ ]
1747
+ },
1748
+ {
1749
+ "id": "2d4499a0-399c-4b6c-970c-7b441498f7b9",
1750
+ "alias": "Reset - Conditional OTP",
1751
+ "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.",
1752
+ "providerId": "basic-flow",
1753
+ "topLevel": false,
1754
+ "builtIn": true,
1755
+ "authenticationExecutions": [
1756
+ {
1757
+ "authenticator": "conditional-user-configured",
1758
+ "authenticatorFlow": false,
1759
+ "requirement": "REQUIRED",
1760
+ "priority": 10,
1761
+ "userSetupAllowed": false,
1762
+ "autheticatorFlow": false
1763
+ },
1764
+ {
1765
+ "authenticator": "reset-otp",
1766
+ "authenticatorFlow": false,
1767
+ "requirement": "REQUIRED",
1768
+ "priority": 20,
1769
+ "userSetupAllowed": false,
1770
+ "autheticatorFlow": false
1771
+ }
1772
+ ]
1773
+ },
1774
+ {
1775
+ "id": "710f4172-56a5-466e-bc75-ad7405ff62b5",
1776
+ "alias": "User creation or linking",
1777
+ "description": "Flow for the existing/non-existing user alternatives",
1778
+ "providerId": "basic-flow",
1779
+ "topLevel": false,
1780
+ "builtIn": true,
1781
+ "authenticationExecutions": [
1782
+ {
1783
+ "authenticatorConfig": "create unique user config",
1784
+ "authenticator": "idp-create-user-if-unique",
1785
+ "authenticatorFlow": false,
1786
+ "requirement": "ALTERNATIVE",
1787
+ "priority": 10,
1788
+ "userSetupAllowed": false,
1789
+ "autheticatorFlow": false
1790
+ },
1791
+ {
1792
+ "authenticatorFlow": true,
1793
+ "requirement": "ALTERNATIVE",
1794
+ "priority": 20,
1795
+ "flowAlias": "Handle Existing Account",
1796
+ "userSetupAllowed": false,
1797
+ "autheticatorFlow": true
1798
+ }
1799
+ ]
1800
+ },
1801
+ {
1802
+ "id": "da7d3a39-7077-4354-9ffc-5b9f79fbaf0d",
1803
+ "alias": "Verify Existing Account by Re-authentication",
1804
+ "description": "Reauthentication of existing account",
1805
+ "providerId": "basic-flow",
1806
+ "topLevel": false,
1807
+ "builtIn": true,
1808
+ "authenticationExecutions": [
1809
+ {
1810
+ "authenticator": "idp-username-password-form",
1811
+ "authenticatorFlow": false,
1812
+ "requirement": "REQUIRED",
1813
+ "priority": 10,
1814
+ "userSetupAllowed": false,
1815
+ "autheticatorFlow": false
1816
+ },
1817
+ {
1818
+ "authenticatorFlow": true,
1819
+ "requirement": "CONDITIONAL",
1820
+ "priority": 20,
1821
+ "flowAlias": "First broker login - Conditional OTP",
1822
+ "userSetupAllowed": false,
1823
+ "autheticatorFlow": true
1824
+ }
1825
+ ]
1826
+ },
1827
+ {
1828
+ "id": "6285968e-6200-463a-a329-8c60bc8fe9fc",
1829
+ "alias": "browser",
1830
+ "description": "browser based authentication",
1831
+ "providerId": "basic-flow",
1832
+ "topLevel": true,
1833
+ "builtIn": true,
1834
+ "authenticationExecutions": [
1835
+ {
1836
+ "authenticator": "auth-cookie",
1837
+ "authenticatorFlow": false,
1838
+ "requirement": "ALTERNATIVE",
1839
+ "priority": 10,
1840
+ "userSetupAllowed": false,
1841
+ "autheticatorFlow": false
1842
+ },
1843
+ {
1844
+ "authenticator": "auth-spnego",
1845
+ "authenticatorFlow": false,
1846
+ "requirement": "DISABLED",
1847
+ "priority": 20,
1848
+ "userSetupAllowed": false,
1849
+ "autheticatorFlow": false
1850
+ },
1851
+ {
1852
+ "authenticator": "identity-provider-redirector",
1853
+ "authenticatorFlow": false,
1854
+ "requirement": "ALTERNATIVE",
1855
+ "priority": 25,
1856
+ "userSetupAllowed": false,
1857
+ "autheticatorFlow": false
1858
+ },
1859
+ {
1860
+ "authenticatorFlow": true,
1861
+ "requirement": "ALTERNATIVE",
1862
+ "priority": 30,
1863
+ "flowAlias": "forms",
1864
+ "userSetupAllowed": false,
1865
+ "autheticatorFlow": true
1866
+ }
1867
+ ]
1868
+ },
1869
+ {
1870
+ "id": "10393f04-3922-40db-a622-2655dfcae45d",
1871
+ "alias": "clients",
1872
+ "description": "Base authentication for clients",
1873
+ "providerId": "client-flow",
1874
+ "topLevel": true,
1875
+ "builtIn": true,
1876
+ "authenticationExecutions": [
1877
+ {
1878
+ "authenticator": "client-secret",
1879
+ "authenticatorFlow": false,
1880
+ "requirement": "ALTERNATIVE",
1881
+ "priority": 10,
1882
+ "userSetupAllowed": false,
1883
+ "autheticatorFlow": false
1884
+ },
1885
+ {
1886
+ "authenticator": "client-jwt",
1887
+ "authenticatorFlow": false,
1888
+ "requirement": "ALTERNATIVE",
1889
+ "priority": 20,
1890
+ "userSetupAllowed": false,
1891
+ "autheticatorFlow": false
1892
+ },
1893
+ {
1894
+ "authenticator": "client-secret-jwt",
1895
+ "authenticatorFlow": false,
1896
+ "requirement": "ALTERNATIVE",
1897
+ "priority": 30,
1898
+ "userSetupAllowed": false,
1899
+ "autheticatorFlow": false
1900
+ },
1901
+ {
1902
+ "authenticator": "client-x509",
1903
+ "authenticatorFlow": false,
1904
+ "requirement": "ALTERNATIVE",
1905
+ "priority": 40,
1906
+ "userSetupAllowed": false,
1907
+ "autheticatorFlow": false
1908
+ }
1909
+ ]
1910
+ },
1911
+ {
1912
+ "id": "4e5e164e-3c7e-4ca5-a10c-d7b817a7d468",
1913
+ "alias": "direct grant",
1914
+ "description": "OpenID Connect Resource Owner Grant",
1915
+ "providerId": "basic-flow",
1916
+ "topLevel": true,
1917
+ "builtIn": true,
1918
+ "authenticationExecutions": [
1919
+ {
1920
+ "authenticator": "direct-grant-validate-username",
1921
+ "authenticatorFlow": false,
1922
+ "requirement": "REQUIRED",
1923
+ "priority": 10,
1924
+ "userSetupAllowed": false,
1925
+ "autheticatorFlow": false
1926
+ },
1927
+ {
1928
+ "authenticator": "direct-grant-validate-password",
1929
+ "authenticatorFlow": false,
1930
+ "requirement": "REQUIRED",
1931
+ "priority": 20,
1932
+ "userSetupAllowed": false,
1933
+ "autheticatorFlow": false
1934
+ },
1935
+ {
1936
+ "authenticatorFlow": true,
1937
+ "requirement": "CONDITIONAL",
1938
+ "priority": 30,
1939
+ "flowAlias": "Direct Grant - Conditional OTP",
1940
+ "userSetupAllowed": false,
1941
+ "autheticatorFlow": true
1942
+ }
1943
+ ]
1944
+ },
1945
+ {
1946
+ "id": "80f88b0b-70de-4e4c-ae56-0293558301c5",
1947
+ "alias": "docker auth",
1948
+ "description": "Used by Docker clients to authenticate against the IDP",
1949
+ "providerId": "basic-flow",
1950
+ "topLevel": true,
1951
+ "builtIn": true,
1952
+ "authenticationExecutions": [
1953
+ {
1954
+ "authenticator": "docker-http-basic-authenticator",
1955
+ "authenticatorFlow": false,
1956
+ "requirement": "REQUIRED",
1957
+ "priority": 10,
1958
+ "userSetupAllowed": false,
1959
+ "autheticatorFlow": false
1960
+ }
1961
+ ]
1962
+ },
1963
+ {
1964
+ "id": "821af41a-6e77-4e8c-85a6-0280d5268909",
1965
+ "alias": "first broker login",
1966
+ "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account",
1967
+ "providerId": "basic-flow",
1968
+ "topLevel": true,
1969
+ "builtIn": true,
1970
+ "authenticationExecutions": [
1971
+ {
1972
+ "authenticatorConfig": "review profile config",
1973
+ "authenticator": "idp-review-profile",
1974
+ "authenticatorFlow": false,
1975
+ "requirement": "REQUIRED",
1976
+ "priority": 10,
1977
+ "userSetupAllowed": false,
1978
+ "autheticatorFlow": false
1979
+ },
1980
+ {
1981
+ "authenticatorFlow": true,
1982
+ "requirement": "REQUIRED",
1983
+ "priority": 20,
1984
+ "flowAlias": "User creation or linking",
1985
+ "userSetupAllowed": false,
1986
+ "autheticatorFlow": true
1987
+ }
1988
+ ]
1989
+ },
1990
+ {
1991
+ "id": "c4058fb0-ad93-4595-96ef-7d4bc5cbef4d",
1992
+ "alias": "forms",
1993
+ "description": "Username, password, otp and other auth forms.",
1994
+ "providerId": "basic-flow",
1995
+ "topLevel": false,
1996
+ "builtIn": true,
1997
+ "authenticationExecutions": [
1998
+ {
1999
+ "authenticator": "auth-username-password-form",
2000
+ "authenticatorFlow": false,
2001
+ "requirement": "REQUIRED",
2002
+ "priority": 10,
2003
+ "userSetupAllowed": false,
2004
+ "autheticatorFlow": false
2005
+ },
2006
+ {
2007
+ "authenticatorFlow": true,
2008
+ "requirement": "CONDITIONAL",
2009
+ "priority": 20,
2010
+ "flowAlias": "Browser - Conditional OTP",
2011
+ "userSetupAllowed": false,
2012
+ "autheticatorFlow": true
2013
+ }
2014
+ ]
2015
+ },
2016
+ {
2017
+ "id": "a2a1d056-2521-498f-b345-b7db56f9342c",
2018
+ "alias": "http challenge",
2019
+ "description": "An authentication flow based on challenge-response HTTP Authentication Schemes",
2020
+ "providerId": "basic-flow",
2021
+ "topLevel": true,
2022
+ "builtIn": true,
2023
+ "authenticationExecutions": [
2024
+ {
2025
+ "authenticator": "no-cookie-redirect",
2026
+ "authenticatorFlow": false,
2027
+ "requirement": "REQUIRED",
2028
+ "priority": 10,
2029
+ "userSetupAllowed": false,
2030
+ "autheticatorFlow": false
2031
+ },
2032
+ {
2033
+ "authenticatorFlow": true,
2034
+ "requirement": "REQUIRED",
2035
+ "priority": 20,
2036
+ "flowAlias": "Authentication Options",
2037
+ "userSetupAllowed": false,
2038
+ "autheticatorFlow": true
2039
+ }
2040
+ ]
2041
+ },
2042
+ {
2043
+ "id": "13e68e1b-4b44-4f21-a253-5b2dea24404b",
2044
+ "alias": "registration",
2045
+ "description": "registration flow",
2046
+ "providerId": "basic-flow",
2047
+ "topLevel": true,
2048
+ "builtIn": true,
2049
+ "authenticationExecutions": [
2050
+ {
2051
+ "authenticator": "registration-page-form",
2052
+ "authenticatorFlow": true,
2053
+ "requirement": "REQUIRED",
2054
+ "priority": 10,
2055
+ "flowAlias": "registration form",
2056
+ "userSetupAllowed": false,
2057
+ "autheticatorFlow": true
2058
+ }
2059
+ ]
2060
+ },
2061
+ {
2062
+ "id": "e7588789-22d4-459b-96d6-1b480520f487",
2063
+ "alias": "registration form",
2064
+ "description": "registration form",
2065
+ "providerId": "form-flow",
2066
+ "topLevel": false,
2067
+ "builtIn": true,
2068
+ "authenticationExecutions": [
2069
+ {
2070
+ "authenticator": "registration-user-creation",
2071
+ "authenticatorFlow": false,
2072
+ "requirement": "REQUIRED",
2073
+ "priority": 20,
2074
+ "userSetupAllowed": false,
2075
+ "autheticatorFlow": false
2076
+ },
2077
+ {
2078
+ "authenticator": "registration-profile-action",
2079
+ "authenticatorFlow": false,
2080
+ "requirement": "REQUIRED",
2081
+ "priority": 40,
2082
+ "userSetupAllowed": false,
2083
+ "autheticatorFlow": false
2084
+ },
2085
+ {
2086
+ "authenticator": "registration-password-action",
2087
+ "authenticatorFlow": false,
2088
+ "requirement": "REQUIRED",
2089
+ "priority": 50,
2090
+ "userSetupAllowed": false,
2091
+ "autheticatorFlow": false
2092
+ },
2093
+ {
2094
+ "authenticator": "registration-recaptcha-action",
2095
+ "authenticatorFlow": false,
2096
+ "requirement": "DISABLED",
2097
+ "priority": 60,
2098
+ "userSetupAllowed": false,
2099
+ "autheticatorFlow": false
2100
+ }
2101
+ ]
2102
+ },
2103
+ {
2104
+ "id": "8dc399ef-cf7d-46d5-9688-678c146ea8c4",
2105
+ "alias": "reset credentials",
2106
+ "description": "Reset credentials for a user if they forgot their password or something",
2107
+ "providerId": "basic-flow",
2108
+ "topLevel": true,
2109
+ "builtIn": true,
2110
+ "authenticationExecutions": [
2111
+ {
2112
+ "authenticator": "reset-credentials-choose-user",
2113
+ "authenticatorFlow": false,
2114
+ "requirement": "REQUIRED",
2115
+ "priority": 10,
2116
+ "userSetupAllowed": false,
2117
+ "autheticatorFlow": false
2118
+ },
2119
+ {
2120
+ "authenticator": "reset-credential-email",
2121
+ "authenticatorFlow": false,
2122
+ "requirement": "REQUIRED",
2123
+ "priority": 20,
2124
+ "userSetupAllowed": false,
2125
+ "autheticatorFlow": false
2126
+ },
2127
+ {
2128
+ "authenticator": "reset-password",
2129
+ "authenticatorFlow": false,
2130
+ "requirement": "REQUIRED",
2131
+ "priority": 30,
2132
+ "userSetupAllowed": false,
2133
+ "autheticatorFlow": false
2134
+ },
2135
+ {
2136
+ "authenticatorFlow": true,
2137
+ "requirement": "CONDITIONAL",
2138
+ "priority": 40,
2139
+ "flowAlias": "Reset - Conditional OTP",
2140
+ "userSetupAllowed": false,
2141
+ "autheticatorFlow": true
2142
+ }
2143
+ ]
2144
+ },
2145
+ {
2146
+ "id": "47ab5a7a-f67a-4a66-bdac-932ee230000d",
2147
+ "alias": "saml ecp",
2148
+ "description": "SAML ECP Profile Authentication Flow",
2149
+ "providerId": "basic-flow",
2150
+ "topLevel": true,
2151
+ "builtIn": true,
2152
+ "authenticationExecutions": [
2153
+ {
2154
+ "authenticator": "http-basic-authenticator",
2155
+ "authenticatorFlow": false,
2156
+ "requirement": "REQUIRED",
2157
+ "priority": 10,
2158
+ "userSetupAllowed": false,
2159
+ "autheticatorFlow": false
2160
+ }
2161
+ ]
2162
+ }
2163
+ ],
2164
+ "authenticatorConfig": [
2165
+ {
2166
+ "id": "b12be521-4e2b-42f0-a1a2-f1ba47ab4854",
2167
+ "alias": "create unique user config",
2168
+ "config": {
2169
+ "require.password.update.after.registration": "false"
2170
+ }
2171
+ },
2172
+ {
2173
+ "id": "58bf2d56-1c45-4acc-9005-23b978d961d7",
2174
+ "alias": "review profile config",
2175
+ "config": {
2176
+ "update.profile.on.first.login": "missing"
2177
+ }
2178
+ }
2179
+ ],
2180
+ "requiredActions": [
2181
+ {
2182
+ "alias": "CONFIGURE_TOTP",
2183
+ "name": "Configure OTP",
2184
+ "providerId": "CONFIGURE_TOTP",
2185
+ "enabled": true,
2186
+ "defaultAction": false,
2187
+ "priority": 10,
2188
+ "config": {}
2189
+ },
2190
+ {
2191
+ "alias": "terms_and_conditions",
2192
+ "name": "Terms and Conditions",
2193
+ "providerId": "terms_and_conditions",
2194
+ "enabled": false,
2195
+ "defaultAction": false,
2196
+ "priority": 20,
2197
+ "config": {}
2198
+ },
2199
+ {
2200
+ "alias": "UPDATE_PASSWORD",
2201
+ "name": "Update Password",
2202
+ "providerId": "UPDATE_PASSWORD",
2203
+ "enabled": true,
2204
+ "defaultAction": false,
2205
+ "priority": 30,
2206
+ "config": {}
2207
+ },
2208
+ {
2209
+ "alias": "UPDATE_PROFILE",
2210
+ "name": "Update Profile",
2211
+ "providerId": "UPDATE_PROFILE",
2212
+ "enabled": true,
2213
+ "defaultAction": false,
2214
+ "priority": 40,
2215
+ "config": {}
2216
+ },
2217
+ {
2218
+ "alias": "VERIFY_EMAIL",
2219
+ "name": "Verify Email",
2220
+ "providerId": "VERIFY_EMAIL",
2221
+ "enabled": true,
2222
+ "defaultAction": false,
2223
+ "priority": 50,
2224
+ "config": {}
2225
+ },
2226
+ {
2227
+ "alias": "delete_account",
2228
+ "name": "Delete Account",
2229
+ "providerId": "delete_account",
2230
+ "enabled": false,
2231
+ "defaultAction": false,
2232
+ "priority": 60,
2233
+ "config": {}
2234
+ },
2235
+ {
2236
+ "alias": "update_user_locale",
2237
+ "name": "Update User Locale",
2238
+ "providerId": "update_user_locale",
2239
+ "enabled": true,
2240
+ "defaultAction": false,
2241
+ "priority": 1000,
2242
+ "config": {}
2243
+ },
2244
+ {
2245
+ "alias": "webauthn-register",
2246
+ "name": "Webauthn Register",
2247
+ "providerId": "webauthn-register",
2248
+ "enabled": true,
2249
+ "defaultAction": false,
2250
+ "priority": 70,
2251
+ "config": {}
2252
+ },
2253
+ {
2254
+ "alias": "webauthn-register-passwordless",
2255
+ "name": "Webauthn Register Passwordless",
2256
+ "providerId": "webauthn-register-passwordless",
2257
+ "enabled": true,
2258
+ "defaultAction": false,
2259
+ "priority": 80,
2260
+ "config": {}
2261
+ }
2262
+ ],
2263
+ "browserFlow": "browser",
2264
+ "registrationFlow": "registration",
2265
+ "directGrantFlow": "direct grant",
2266
+ "resetCredentialsFlow": "reset credentials",
2267
+ "clientAuthenticationFlow": "clients",
2268
+ "dockerAuthenticationFlow": "docker auth",
2269
+ "attributes": {
2270
+ "cibaBackchannelTokenDeliveryMode": "poll",
2271
+ "cibaExpiresIn": "120",
2272
+ "cibaAuthRequestedUserHint": "login_hint",
2273
+ "oauth2DeviceCodeLifespan": "600",
2274
+ "clientOfflineSessionMaxLifespan": "0",
2275
+ "oauth2DevicePollingInterval": "5",
2276
+ "clientSessionIdleTimeout": "0",
2277
+ "clientSessionMaxLifespan": "0",
2278
+ "parRequestUriLifespan": "60",
2279
+ "clientOfflineSessionIdleTimeout": "0",
2280
+ "cibaInterval": "5",
2281
+ "realmReusableOtpCode": "false",
2282
+ "frontendUrl": "",
2283
+ "acr.loa.map": "[]"
2284
+ },
2285
+ "keycloakVersion": "23.0.1",
2286
+ "userManagedAccessAllowed": false,
2287
+ "clientProfiles": {
2288
+ "profiles": []
2289
+ },
2290
+ "clientPolicies": {
2291
+ "policies": []
2292
+ },
2293
+ "users": [
2294
+ {
2295
+ "id": "4c973896-5761-41fc-8217-07c5d13a004b",
2296
+ "createdTimestamp": 1505479415590,
2297
+ "username": "admin",
2298
+ "enabled": true,
2299
+ "totp": false,
2300
+ "emailVerified": true,
2301
+ "firstName": "Admin",
2302
+ "lastName": "Administrator",
2303
+ "email": "admin@localhost",
2304
+ "credentials": [
2305
+ {
2306
+ "id": "b860462b-9b02-48ba-9523-d3a8926a917b",
2307
+ "type": "password",
2308
+ "createdDate": 1505479429154,
2309
+ "secretData": "{\"value\":\"4pf9K2jWSCcHC+CwsZP/qidN5pSmDUe6AX6wBerSGdBVKkExay8MWKx+EKmaaObZW6FVsD8vdW/ZsyUFD9gJ1Q==\",\"salt\":\"1/qNkZ5kr77jOMOBPBogGw==\"}",
2310
+ "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}"
2311
+ }
2312
+ ],
2313
+ "disableableCredentialTypes": [],
2314
+ "requiredActions": [],
2315
+ "realmRoles": ["offline_access", "uma_authorization"],
2316
+ "clientRoles": {
2317
+ "account": ["view-profile", "manage-account"]
2318
+ },
2319
+ "notBefore": 0,
2320
+ "groups": ["/Admins", "/Users"]
2321
+ },
2322
+ {
2323
+ "id": "c4af4e2f-b432-4c3b-8405-cca86cd5b97b",
2324
+ "createdTimestamp": 1505479373742,
2325
+ "username": "user",
2326
+ "enabled": true,
2327
+ "totp": false,
2328
+ "emailVerified": true,
2329
+ "firstName": "",
2330
+ "lastName": "User",
2331
+ "email": "user@localhost",
2332
+ "credentials": [
2333
+ {
2334
+ "id": "7821832b-1e82-45a2-b8d3-f1a6ad909e64",
2335
+ "type": "password",
2336
+ "createdDate": 1505479392766,
2337
+ "secretData": "{\"value\":\"MbKsMgWPnZyImih8s4SaoCSCq+XIY/c6S9F93sXEidHF1TjPWxCqMkec0+o3860CMLXHt3az61cIJOWI0FW9aw==\",\"salt\":\"fmpBI1r8R1u75hDLMUlwBw==\"}",
2338
+ "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\"}"
2339
+ }
2340
+ ],
2341
+ "disableableCredentialTypes": [],
2342
+ "requiredActions": [],
2343
+ "realmRoles": ["offline_access", "uma_authorization"],
2344
+ "clientRoles": {
2345
+ "account": ["view-profile", "manage-account"]
2346
+ },
2347
+ "notBefore": 0,
2348
+ "groups": ["/Users"]
2349
+ }
2350
+ ]
2351
+ }
src/main/docker/realm-config/keycloak-health-check.sh ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ exec 3<>/dev/tcp/localhost/9080
3
+
4
+ echo -e "GET /health/ready HTTP/1.1\nhost: localhost:9080\n" >&3
5
+
6
+ timeout --preserve-status 1 cat <&3 | grep -m 1 status | grep -m 1 UP
7
+ ERROR=$?
8
+
9
+ exec 3<&-
10
+ exec 3>&-
11
+
12
+ exit $ERROR
src/main/docker/services.yml ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production
2
+ name: da-discovery
3
+ services:
4
+ postgresql:
5
+ extends:
6
+ file: ./postgresql.yml
7
+ service: postgresql
8
+ networks:
9
+ - da-discovery-network
10
+
11
+ zookeeper:
12
+ extends:
13
+ file: ./kafka.yml
14
+ service: zookeeper
15
+ networks:
16
+ - da-discovery-network
17
+
18
+ kafka:
19
+ extends:
20
+ file: ./kafka.yml
21
+ service: kafka
22
+ networks:
23
+ - da-discovery-network
24
+ depends_on:
25
+ - zookeeper
26
+
27
+ keycloak:
28
+ extends:
29
+ file: ./keycloak.yml
30
+ service: keycloak
31
+ networks:
32
+ - da-discovery-network
33
+
34
+ jhipster-registry:
35
+ extends:
36
+ file: ./jhipster-registry.yml
37
+ service: jhipster-registry
38
+ depends_on:
39
+ keycloak:
40
+ condition: service_healthy
41
+ networks:
42
+ - da-discovery-network
43
+
44
+ networks:
45
+ da-discovery-network:
46
+ driver: bridge
47
+
48
+ volumes:
49
+ postgresql-data:
50
+ kafka-data:
51
+ keycloak-data:
52
+ jhipster-registry-data:
src/main/docker/shell-wrapper.sh ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ # Shell wrapper script to execute Spring Shell commands
4
+ # Usage: ./shell-wrapper.sh list-crawlers
5
+
6
+ # If no arguments provided, run in interactive shell mode
7
+ if [ $# -eq 0 ]; then
8
+ java -jar /app/app.jar shell --spring.profiles.active=shell
9
+ else
10
+ # Run the specific command
11
+ java -jar /app/app.jar "$@" --spring.profiles.active=shell
12
+ fi
src/main/docker/sonar.yml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production
2
+ name: da-discovery
3
+ services:
4
+ sonar:
5
+ container_name: sonarqube
6
+ image: sonarqube:10.3.0-community
7
+ # Forced authentication redirect for UI is turned off for out of the box experience while trying out SonarQube
8
+ # For real use cases delete SONAR_FORCEAUTHENTICATION variable or set SONAR_FORCEAUTHENTICATION=true
9
+ environment:
10
+ - SONAR_FORCEAUTHENTICATION=false
11
+ # If you want to expose these ports outside your dev PC,
12
+ # remove the "127.0.0.1:" prefix
13
+ ports:
14
+ - 127.0.0.1:9001:9000
15
+ - 127.0.0.1:9000:9000
src/main/docker/zipkin.yml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production
2
+ name: da-discovery
3
+ services:
4
+ zipkin:
5
+ image: openzipkin/zipkin:3.0
6
+ ports:
7
+ - 127.0.0.1:9411:9411
src/main/java/com/dalab/discovery/application/DADiscoveryAgent.java ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.application;
2
+
3
+ import java.net.InetAddress;
4
+ import java.net.UnknownHostException;
5
+ import java.util.Arrays;
6
+ import java.util.Collection;
7
+ import java.util.HashMap;
8
+ import java.util.Optional;
9
+ import java.util.Map;
10
+
11
+ import org.apache.commons.lang3.StringUtils;
12
+ import org.slf4j.Logger;
13
+ import org.slf4j.LoggerFactory;
14
+ import org.springframework.beans.factory.annotation.Autowired;
15
+ import org.springframework.beans.factory.annotation.Value;
16
+ import org.springframework.boot.SpringApplication;
17
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
18
+ import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties;
19
+ import org.springframework.boot.context.properties.EnableConfigurationProperties;
20
+ import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
21
+ import org.springframework.cloud.openfeign.EnableFeignClients;
22
+ import org.springframework.context.annotation.ComponentScan;
23
+ import org.springframework.context.annotation.EnableAspectJAutoProxy;
24
+ import org.springframework.core.env.Environment;
25
+ import org.springframework.scheduling.annotation.EnableScheduling;
26
+ import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
27
+ import org.springframework.shell.command.annotation.EnableCommand;
28
+
29
+ import com.dalab.discovery.catalog.service.ICatalogService;
30
+ //import org.springframework.shell.command.annotation.EnableCommand;
31
+ import com.dalab.discovery.client.cli.DiscoveryCommands;
32
+ import com.dalab.discovery.common.config.ApplicationProperties;
33
+ import com.dalab.discovery.common.config.CRLFLogConverter;
34
+ import com.dalab.discovery.common.config.DynamicConfig;
35
+ import com.dalab.discovery.crawler.model.gcp.BigQueryResource;
36
+ import com.dalab.discovery.crawler.service.IDiscoveryService;
37
+ import com.dalab.discovery.crawler.service.IFolderManagementService;
38
+ import com.dalab.discovery.crawler.service.gcp.GCPResourceCrawler;
39
+ import com.dalab.discovery.log.service.ILogAnalyzer;
40
+
41
+ import jakarta.annotation.PostConstruct;
42
+ import tech.jhipster.config.DefaultProfileUtil;
43
+ import tech.jhipster.config.JHipsterConstants;
44
+
45
+ @SpringBootApplication
46
+ @EnableConfigurationProperties({ LiquibaseProperties.class, ApplicationProperties.class, DynamicConfig.class })
47
+ @EnableDiscoveryClient
48
+ @EnableFeignClients(basePackages = "com.dalab.discovery.client.feign")
49
+ @EnableScheduling
50
+ @EnableAspectJAutoProxy(proxyTargetClass = true)
51
+ @EnableCommand(DiscoveryCommands.class)
52
+ @ComponentScan(basePackages = { "com.dalab.discovery" })
53
+ @EnableMethodSecurity
54
+ public class DADiscoveryAgent {
55
+
56
+ @Value("${unity.catalog.name}")
57
+ private String catalogName;
58
+
59
+ @Value("${unity.catalog.schema.name}")
60
+ private String schemaName;
61
+
62
+ @Autowired
63
+ private final IFolderManagementService folderManagementService;
64
+
65
+ @Autowired
66
+ private GCPResourceCrawler<BigQueryResource> gcpResourceCrawler;
67
+
68
+ private static final Logger log = LoggerFactory.getLogger(DADiscoveryAgent.class);
69
+
70
+ private final Environment env;
71
+ private final DynamicConfig dynamicConfig;
72
+ private final ICatalogService catalogService;
73
+ private final Collection<IDiscoveryService<?>> discoveryServices;
74
+ private Collection<ILogAnalyzer> logAnalyzerServices;
75
+
76
+ public DADiscoveryAgent(Environment env, DynamicConfig dynamicConfig,
77
+ ICatalogService unityCatalogManager, Collection<IDiscoveryService<?>> discoveryServices,
78
+ Collection<ILogAnalyzer> logAnalyzerServices,IFolderManagementService folderManagementService,
79
+ GCPResourceCrawler gcpResourceCrawler) {
80
+ this.gcpResourceCrawler = gcpResourceCrawler;
81
+ this.folderManagementService = folderManagementService;
82
+ this.env = env;
83
+ this.dynamicConfig = dynamicConfig;
84
+ this.catalogService = unityCatalogManager;
85
+ this.discoveryServices = discoveryServices;
86
+ this.logAnalyzerServices = logAnalyzerServices;
87
+ }
88
+
89
+
90
+ public void runGCPCrawler(){
91
+
92
+ //String folderId = dynamicConfig.getFolderId();
93
+
94
+ String folderId = "621021804931";
95
+ String accountId = "aialabs-dg-dev";
96
+ // Prepare context for BigQueryCrawler
97
+ Map<String, Object> context = new HashMap<>();
98
+ context.put("projectId", accountId); // Ensure correct project ID
99
+ context.put("location", "us-central1"); // Optional
100
+
101
+
102
+
103
+ gcpResourceCrawler.discoverResourcesAsync(accountId, context);
104
+
105
+ //folderManagementService.listFolders(folderId);
106
+
107
+
108
+
109
+
110
+
111
+
112
+ }
113
+
114
+ /**
115
+ * Initializes DGCrawler.
116
+ * <p>
117
+ * Spring profiles can be configured with a program argument
118
+ * --spring.profiles.active=your-active-profile
119
+ * <p>
120
+ * You can find more information on how profiles work with JHipster on <a href=
121
+ * "https://www.jhipster.tech/profiles/">https://www.jhipster.tech/profiles/</a>.
122
+ */
123
+ @PostConstruct
124
+ public void initApplication() {
125
+ Collection<String> activeProfiles = Arrays.asList(env.getActiveProfiles());
126
+ if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) &&
127
+ activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
128
+ log.error(
129
+ "You have misconfigured your application! It should not run "
130
+ + "with both the 'dev' and 'prod' profiles at the same time.");
131
+ }
132
+ if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) &&
133
+ activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD)) {
134
+ log.error(
135
+ "You have misconfigured your application! It should not "
136
+ + "run with both the 'dev' and 'cloud' profiles at the same time.");
137
+ }
138
+
139
+ runGCPCrawler();
140
+
141
+ bootupDiscoveryServices(discoveryServices);
142
+ bootupLogAnalyzerServices(logAnalyzerServices);
143
+ }
144
+
145
+ /**
146
+ * Main method, used to run the application.
147
+ *
148
+ * @param args the command line arguments.
149
+ */
150
+ public static void main(String[] args) {
151
+ // Determine if we're running a shell command or regular application
152
+ boolean isShellCommand = true;
153
+ if (args.length > 0 && !args[0].startsWith("-")) {
154
+ // This is likely a shell command, not a regular Spring Boot argument
155
+ isShellCommand = true;
156
+ System.setProperty("spring.shell.interactive.enabled", "true");
157
+ } else {
158
+ // This is a regular Spring Boot application, disable Spring Shell
159
+ System.setProperty("spring.shell.interactive.enabled", "false");
160
+ System.setProperty("spring.shell.noninteractive.enabled", "false");
161
+ }
162
+ log.info("Starting application in {} mode", isShellCommand ? "shell" : "regular");
163
+ // Initialize Spring application
164
+ SpringApplication app = new SpringApplication(DADiscoveryAgent.class);
165
+
166
+ // Add default profile if needed
167
+ DefaultProfileUtil.addDefaultProfile(app);
168
+
169
+ // Run the application
170
+ Environment env = app.run(args).getEnvironment();
171
+
172
+ // Log application startup
173
+ logApplicationStartup(env);
174
+ }
175
+
176
+ /**
177
+ * Bootup and configure the discovery services.
178
+ *
179
+ * @param discoveryServices
180
+ */
181
+ private void bootupDiscoveryServices(Collection<IDiscoveryService<?>> discoveryServices) {
182
+ }
183
+
184
+ /**
185
+ * Bootup and configure the log analyzer services.
186
+ *
187
+ * @param logAnalyzerServices
188
+ */
189
+ private void bootupLogAnalyzerServices(Collection<ILogAnalyzer> logAnalyzerServices) {
190
+ }
191
+
192
+ private static void logApplicationStartup(Environment env) {
193
+ String protocol = Optional.ofNullable(env.getProperty("server.ssl.key-store")).map(key -> "https")
194
+ .orElse("http");
195
+ String applicationName = env.getProperty("spring.application.name");
196
+ String serverPort = env.getProperty("server.port");
197
+ String contextPath = Optional
198
+ .ofNullable(env.getProperty("server.servlet.context-path"))
199
+ .filter(StringUtils::isNotBlank)
200
+ .orElse("/");
201
+ String hostAddress = "localhost";
202
+ try {
203
+ hostAddress = InetAddress.getLocalHost().getHostAddress();
204
+ } catch (UnknownHostException e) {
205
+ log.warn("The host name could not be determined, using `localhost` as fallback");
206
+ }
207
+ log.info(
208
+ CRLFLogConverter.CRLF_SAFE_MARKER,
209
+ """
210
+
211
+ ----------------------------------------------------------
212
+ \tApplication '{}' is running! Access URLs:
213
+ \tLocal: \t\t{}://localhost:{}{}
214
+ \tExternal: \t{}://{}:{}{}
215
+ \tProfile(s): \t{}
216
+ ----------------------------------------------------------""",
217
+ applicationName,
218
+ protocol,
219
+ serverPort,
220
+ contextPath,
221
+ protocol,
222
+ hostAddress,
223
+ serverPort,
224
+ contextPath,
225
+ env.getActiveProfiles().length == 0 ? env.getDefaultProfiles()
226
+ : env.getActiveProfiles());
227
+
228
+ String configServerStatus = env.getProperty("configserver.status");
229
+ if (configServerStatus == null) {
230
+ configServerStatus = "Not found or not setup for this application";
231
+ }
232
+ log.info(
233
+ CRLFLogConverter.CRLF_SAFE_MARKER,
234
+ "\n----------------------------------------------------------\n\t" +
235
+ "Config Server: \t{}\n----------------------------------------------------------",
236
+ configServerStatus);
237
+ }
238
+ }
src/main/java/com/dalab/discovery/catalog/client/CatalogClient.java ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.client;
2
+
3
+ import java.util.List;
4
+ import java.util.Map;
5
+ import java.util.Optional;
6
+
7
+ import com.dalab.discovery.common.model.CloudResource;
8
+ import com.dalab.discovery.common.model.CloudService;
9
+ import com.dalab.discovery.common.model.ResourceChange;
10
+ import com.dalab.discovery.common.model.ResourceType;
11
+
12
+ /**
13
+ * Client interface for interacting with external catalog services.
14
+ * This provides a common interface for different catalog services,
15
+ * such as data catalogs, metadata stores, or asset inventory systems.
16
+ */
17
+ public interface CatalogClient {
18
+
19
+ /**
20
+ * Registers a resource in the catalog.
21
+ *
22
+ * @param resource The resource to register
23
+ * @return The ID of the registered resource in the catalog
24
+ */
25
+ String registerResource(CloudResource resource);
26
+
27
+ /**
28
+ * Registers multiple resources in the catalog.
29
+ *
30
+ * @param resources The resources to register
31
+ * @return A map of resource IDs to their catalog IDs
32
+ */
33
+ Map<String, String> registerResources(List<CloudResource> resources);
34
+
35
+ /**
36
+ * Updates a resource in the catalog.
37
+ *
38
+ * @param resource The resource to update
39
+ * @return true if the resource was updated, false otherwise
40
+ */
41
+ boolean updateResource(CloudResource resource);
42
+
43
+ /**
44
+ * Gets a resource from the catalog.
45
+ *
46
+ * @param resourceId The resource ID (usually the unique ID like OCID, ARN,
47
+ * etc.)
48
+ * @param resourceType The resource type record (contains ID, service, provider
49
+ * info)
50
+ * @return The resource, if found
51
+ */
52
+ Optional<CloudResource> getResource(String resourceId, ResourceType resourceType);
53
+
54
+ /**
55
+ * Searches for resources in the catalog.
56
+ *
57
+ * @param query The search query
58
+ * @param parameters Additional search parameters
59
+ * @return The resources matching the search
60
+ */
61
+ List<CloudResource> searchResources(String query, Map<String, Object> parameters);
62
+
63
+ /**
64
+ * Deletes a resource from the catalog.
65
+ *
66
+ * @param resourceId The resource ID
67
+ * @param resourceType The resource type record
68
+ * @return true if the resource was deleted, false otherwise
69
+ */
70
+ boolean deleteResource(String resourceId, ResourceType resourceType);
71
+
72
+ /**
73
+ * Registers a schema for a resource.
74
+ *
75
+ * @param resourceId The resource ID
76
+ * @param schema The schema definition
77
+ * @return The ID of the registered schema
78
+ */
79
+ String registerSchema(String resourceId, Object schema);
80
+
81
+ /**
82
+ * Gets the schema for a resource.
83
+ *
84
+ * @param resourceId The resource ID
85
+ * @return The schema, if found
86
+ */
87
+ Optional<Object> getSchema(String resourceId);
88
+
89
+ /**
90
+ * Records lineage information.
91
+ *
92
+ * @param sourceResourceId The source resource ID
93
+ * @param targetResourceId The target resource ID
94
+ * @param lineageType The type of lineage (e.g., "COPY", "DERIVE",
95
+ * "TRANSFORM")
96
+ * @param metadata Additional lineage metadata
97
+ * @return The ID of the lineage record
98
+ */
99
+ String recordLineage(String sourceResourceId, String targetResourceId,
100
+ String lineageType, Map<String, Object> metadata);
101
+
102
+ /**
103
+ * Gets lineage information for a resource.
104
+ *
105
+ * @param resourceId The resource ID
106
+ * @param direction The lineage direction ("UPSTREAM", "DOWNSTREAM", or "BOTH")
107
+ * @param depth The maximum depth to traverse
108
+ * @return The lineage information
109
+ */
110
+ Map<String, Object> getLineage(String resourceId, String direction, int depth);
111
+
112
+ /**
113
+ * Adds tags to a resource.
114
+ *
115
+ * @param resourceId The resource ID
116
+ * @param tags The tags to add
117
+ * @return true if the tags were added, false otherwise
118
+ */
119
+ boolean addTags(String resourceId, Map<String, String> tags);
120
+
121
+ /**
122
+ * Gets tags for a resource.
123
+ *
124
+ * @param resourceId The resource ID
125
+ * @return The tags for the resource
126
+ */
127
+ Map<String, String> getTags(String resourceId);
128
+
129
+ /**
130
+ * Records an access event for a resource.
131
+ *
132
+ * @param resourceId The resource ID
133
+ * @param userId The user ID
134
+ * @param accessType The type of access (e.g., "READ", "WRITE")
135
+ * @param timestamp The time of access (null for current time)
136
+ * @return true if the access was recorded, false otherwise
137
+ */
138
+ boolean recordAccess(String resourceId, String userId, String accessType, Long timestamp);
139
+
140
+ /**
141
+ * Gets access history for a resource.
142
+ *
143
+ * @param resourceId The resource ID
144
+ * @param limit The maximum number of records to return
145
+ * @return The access history
146
+ */
147
+ List<Map<String, Object>> getAccessHistory(String resourceId, int limit);
148
+
149
+ /**
150
+ * Records a change to a resource.
151
+ *
152
+ * @param change The resource change
153
+ * @return The ID of the change record
154
+ */
155
+ String recordChange(ResourceChange change);
156
+
157
+ /**
158
+ * Gets change history for a resource.
159
+ *
160
+ * @param resourceId The resource ID
161
+ * @param limit The maximum number of records to return
162
+ * @return The change history
163
+ */
164
+ List<ResourceChange> getChangeHistory(String resourceId, int limit);
165
+
166
+ /**
167
+ * Gets the catalog connection status.
168
+ *
169
+ * @return true if connected, false otherwise
170
+ */
171
+ boolean isConnected();
172
+
173
+ /**
174
+ * Gets information about the catalog service.
175
+ *
176
+ * @return Information about the catalog service
177
+ */
178
+ Map<String, Object> getCatalogInfo();
179
+
180
+ /**
181
+ * Gets all resource types from the catalog.
182
+ *
183
+ * @return List of all available resource types
184
+ */
185
+ List<ResourceType> getAllResourceTypes();
186
+
187
+ /**
188
+ * Gets all cloud services from the catalog.
189
+ *
190
+ * @return List of all available cloud services
191
+ */
192
+ List<CloudService> getAllCloudServices();
193
+
194
+ /**
195
+ * Gets a specific resource type by its ID.
196
+ *
197
+ * @param typeId The resource type ID
198
+ * @return The resource type if found, null otherwise
199
+ */
200
+ ResourceType getResourceType(String typeId);
201
+
202
+ /**
203
+ * Gets a specific cloud service by its ID.
204
+ *
205
+ * @param serviceId The service ID
206
+ * @return The cloud service if found, null otherwise
207
+ */
208
+ CloudService getCloudService(String serviceId);
209
+
210
+ /**
211
+ * Gets service mappings from the catalog.
212
+ * These mappings help translate between different cloud provider service
213
+ * identifiers.
214
+ *
215
+ * @return Map of service mappings
216
+ */
217
+ Map<String, Object> getServiceMappings();
218
+ }
src/main/java/com/dalab/discovery/catalog/client/impl/DefaultCatalogClient.java ADDED
@@ -0,0 +1,563 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.client.impl;
2
+
3
+ import java.util.Collections;
4
+ import java.util.HashMap;
5
+ import java.util.List;
6
+ import java.util.Map;
7
+ import java.util.Optional;
8
+ import java.util.stream.Collectors;
9
+
10
+ import org.slf4j.Logger;
11
+ import org.slf4j.LoggerFactory;
12
+ import org.springframework.beans.factory.annotation.Value;
13
+ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
14
+ import org.springframework.http.HttpEntity;
15
+ import org.springframework.http.HttpHeaders;
16
+ import org.springframework.http.HttpMethod;
17
+ import org.springframework.http.MediaType;
18
+ import org.springframework.http.ResponseEntity;
19
+ import org.springframework.stereotype.Service;
20
+ import org.springframework.web.client.RestClientException;
21
+ import org.springframework.web.client.RestTemplate;
22
+
23
+ import com.dalab.discovery.catalog.client.CatalogClient;
24
+ import com.dalab.discovery.common.model.CloudResource;
25
+ import com.dalab.discovery.common.model.CloudService;
26
+ import com.dalab.discovery.common.model.ResourceChange;
27
+ import com.dalab.discovery.common.model.ResourceType;
28
+
29
+ /**
30
+ * Default implementation of the CatalogClient interface.
31
+ * This implementation uses REST API calls to interact with an external catalog
32
+ * service.
33
+ */
34
+ @Service
35
+ @ConditionalOnProperty(name = "catalog.external.enabled", havingValue = "true", matchIfMissing = false)
36
+ public class DefaultCatalogClient implements CatalogClient {
37
+ private static final Logger logger = LoggerFactory.getLogger(DefaultCatalogClient.class);
38
+
39
+ private final RestTemplate restTemplate;
40
+ private final String catalogBaseUrl;
41
+ private final String apiKey;
42
+
43
+ public DefaultCatalogClient(
44
+ RestTemplate restTemplate,
45
+ @Value("${catalog.external.baseUrl}") String catalogBaseUrl,
46
+ @Value("${catalog.external.apiKey:#{null}}") String apiKey) {
47
+ this.restTemplate = restTemplate;
48
+ this.catalogBaseUrl = catalogBaseUrl;
49
+ this.apiKey = apiKey;
50
+ logger.info("Initialized DefaultCatalogClient with base URL: {}", catalogBaseUrl);
51
+ }
52
+
53
+ private HttpHeaders createHeaders() {
54
+ HttpHeaders headers = new HttpHeaders();
55
+ headers.setContentType(MediaType.APPLICATION_JSON);
56
+ headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
57
+ if (apiKey != null && !apiKey.isBlank()) {
58
+ headers.set("X-API-Key", apiKey);
59
+ }
60
+ return headers;
61
+ }
62
+
63
+ @Override
64
+ public String registerResource(CloudResource resource) {
65
+ try {
66
+ HttpEntity<CloudResource> entity = new HttpEntity<>(resource, createHeaders());
67
+ ResponseEntity<Map> response = restTemplate.exchange(
68
+ catalogBaseUrl + "/resources",
69
+ HttpMethod.POST,
70
+ entity,
71
+ Map.class);
72
+
73
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
74
+ return (String) response.getBody().get("catalogId");
75
+ }
76
+ logger.warn("Failed to register resource: {}", resource.getId());
77
+ return null;
78
+ } catch (Exception e) {
79
+ logger.error("Error registering resource: " + resource.getId(), e);
80
+ return null;
81
+ }
82
+ }
83
+
84
+ @Override
85
+ public Map<String, String> registerResources(List<CloudResource> resources) {
86
+ try {
87
+ HttpEntity<List<CloudResource>> entity = new HttpEntity<>(resources, createHeaders());
88
+ ResponseEntity<Map> response = restTemplate.exchange(
89
+ catalogBaseUrl + "/resources/batch",
90
+ HttpMethod.POST,
91
+ entity,
92
+ Map.class);
93
+
94
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
95
+ Map<String, String> result = new HashMap<>();
96
+ Map<String, String> responseMap = (Map<String, String>) response.getBody().get("catalogIds");
97
+ if (responseMap != null) {
98
+ return responseMap;
99
+ }
100
+ }
101
+ logger.warn("Failed to batch register resources, count: {}", resources.size());
102
+ return Collections.emptyMap();
103
+ } catch (Exception e) {
104
+ logger.error("Error batch registering resources", e);
105
+ return Collections.emptyMap();
106
+ }
107
+ }
108
+
109
+ @Override
110
+ public boolean updateResource(CloudResource resource) {
111
+ try {
112
+ HttpEntity<CloudResource> entity = new HttpEntity<>(resource, createHeaders());
113
+ ResponseEntity<Map> response = restTemplate.exchange(
114
+ catalogBaseUrl + "/resources/" + resource.getId(),
115
+ HttpMethod.PUT,
116
+ entity,
117
+ Map.class);
118
+
119
+ return response.getStatusCode().is2xxSuccessful();
120
+ } catch (Exception e) {
121
+ logger.error("Error updating resource: " + resource.getId(), e);
122
+ return false;
123
+ }
124
+ }
125
+
126
+ @Override
127
+ public Optional<CloudResource> getResource(String resourceId, ResourceType resourceType) {
128
+ if (resourceId == null || resourceType == null) {
129
+ logger.warn("Attempted to get resource with null ID or type.");
130
+ return Optional.empty();
131
+ }
132
+ try {
133
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
134
+ String url = catalogBaseUrl + "/resources/" + resourceId + "?type=" + resourceType.id();
135
+ ResponseEntity<CloudResource> response = restTemplate.exchange(
136
+ url,
137
+ HttpMethod.GET,
138
+ entity,
139
+ CloudResource.class);
140
+
141
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
142
+ return Optional.of(response.getBody());
143
+ }
144
+ return Optional.empty();
145
+ } catch (Exception e) {
146
+ logger.error("Error getting resource: " + resourceId + " with type " + resourceType.id(), e);
147
+ return Optional.empty();
148
+ }
149
+ }
150
+
151
+ @Override
152
+ public List<CloudResource> searchResources(String query, Map<String, Object> parameters) {
153
+ try {
154
+ Map<String, Object> requestBody = new HashMap<>();
155
+ requestBody.put("query", query);
156
+ requestBody.put("parameters", parameters);
157
+
158
+ HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, createHeaders());
159
+ ResponseEntity<Map> response = restTemplate.exchange(
160
+ catalogBaseUrl + "/resources/search",
161
+ HttpMethod.POST,
162
+ entity,
163
+ Map.class);
164
+
165
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
166
+ List<Map<String, Object>> resources = (List<Map<String, Object>>) response.getBody().get("resources");
167
+ if (resources != null) {
168
+ // This would need a proper mapper from Map to CloudResource
169
+ return resources.stream()
170
+ .map(this::mapToCloudResource)
171
+ .collect(Collectors.toList());
172
+ }
173
+ }
174
+ return Collections.emptyList();
175
+ } catch (Exception e) {
176
+ logger.error("Error searching resources", e);
177
+ return Collections.emptyList();
178
+ }
179
+ }
180
+
181
+ // This is a placeholder method - would need proper implementation
182
+ private CloudResource mapToCloudResource(Map<String, Object> resourceMap) {
183
+ // In a real implementation, would convert the map to a CloudResource object
184
+ return null;
185
+ }
186
+
187
+ @Override
188
+ public boolean deleteResource(String resourceId, ResourceType resourceType) {
189
+ if (resourceId == null || resourceType == null) {
190
+ logger.warn("Attempted to delete resource with null ID or type.");
191
+ return false;
192
+ }
193
+ try {
194
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
195
+ String url = catalogBaseUrl + "/resources/" + resourceId + "?type=" + resourceType.id();
196
+ ResponseEntity<Void> response = restTemplate.exchange(
197
+ url,
198
+ HttpMethod.DELETE,
199
+ entity,
200
+ Void.class);
201
+
202
+ return response.getStatusCode().is2xxSuccessful();
203
+ } catch (Exception e) {
204
+ logger.error("Error deleting resource: " + resourceId + " with type " + resourceType.id(), e);
205
+ return false;
206
+ }
207
+ }
208
+
209
+ @Override
210
+ public String registerSchema(String resourceId, Object schema) {
211
+ try {
212
+ Map<String, Object> requestBody = new HashMap<>();
213
+ requestBody.put("resourceId", resourceId);
214
+ requestBody.put("schema", schema);
215
+
216
+ HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, createHeaders());
217
+ ResponseEntity<Map> response = restTemplate.exchange(
218
+ catalogBaseUrl + "/schemas",
219
+ HttpMethod.POST,
220
+ entity,
221
+ Map.class);
222
+
223
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
224
+ return (String) response.getBody().get("schemaId");
225
+ }
226
+ return null;
227
+ } catch (Exception e) {
228
+ logger.error("Error registering schema for resource: " + resourceId, e);
229
+ return null;
230
+ }
231
+ }
232
+
233
+ @Override
234
+ public Optional<Object> getSchema(String resourceId) {
235
+ try {
236
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
237
+ ResponseEntity<Map> response = restTemplate.exchange(
238
+ catalogBaseUrl + "/schemas/" + resourceId,
239
+ HttpMethod.GET,
240
+ entity,
241
+ Map.class);
242
+
243
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
244
+ return Optional.ofNullable(response.getBody().get("schema"));
245
+ }
246
+ return Optional.empty();
247
+ } catch (Exception e) {
248
+ logger.error("Error getting schema for resource: " + resourceId, e);
249
+ return Optional.empty();
250
+ }
251
+ }
252
+
253
+ @Override
254
+ public String recordLineage(String sourceResourceId, String targetResourceId,
255
+ String lineageType, Map<String, Object> metadata) {
256
+ try {
257
+ Map<String, Object> requestBody = new HashMap<>();
258
+ requestBody.put("sourceResourceId", sourceResourceId);
259
+ requestBody.put("targetResourceId", targetResourceId);
260
+ requestBody.put("lineageType", lineageType);
261
+ requestBody.put("metadata", metadata);
262
+
263
+ HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, createHeaders());
264
+ ResponseEntity<Map> response = restTemplate.exchange(
265
+ catalogBaseUrl + "/lineage",
266
+ HttpMethod.POST,
267
+ entity,
268
+ Map.class);
269
+
270
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
271
+ return (String) response.getBody().get("lineageId");
272
+ }
273
+ return null;
274
+ } catch (Exception e) {
275
+ logger.error("Error recording lineage", e);
276
+ return null;
277
+ }
278
+ }
279
+
280
+ @Override
281
+ public Map<String, Object> getLineage(String resourceId, String direction, int depth) {
282
+ try {
283
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
284
+ ResponseEntity<Map> response = restTemplate.exchange(
285
+ catalogBaseUrl + "/lineage/" + resourceId +
286
+ "?direction=" + direction + "&depth=" + depth,
287
+ HttpMethod.GET,
288
+ entity,
289
+ Map.class);
290
+
291
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
292
+ return response.getBody();
293
+ }
294
+ return Collections.emptyMap();
295
+ } catch (Exception e) {
296
+ logger.error("Error getting lineage for resource: " + resourceId, e);
297
+ return Collections.emptyMap();
298
+ }
299
+ }
300
+
301
+ @Override
302
+ public boolean addTags(String resourceId, Map<String, String> tags) {
303
+ try {
304
+ HttpEntity<Map<String, String>> entity = new HttpEntity<>(tags, createHeaders());
305
+ ResponseEntity<Map> response = restTemplate.exchange(
306
+ catalogBaseUrl + "/resources/" + resourceId + "/tags",
307
+ HttpMethod.POST,
308
+ entity,
309
+ Map.class);
310
+
311
+ return response.getStatusCode().is2xxSuccessful();
312
+ } catch (Exception e) {
313
+ logger.error("Error adding tags to resource: " + resourceId, e);
314
+ return false;
315
+ }
316
+ }
317
+
318
+ @Override
319
+ public Map<String, String> getTags(String resourceId) {
320
+ try {
321
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
322
+ ResponseEntity<Map> response = restTemplate.exchange(
323
+ catalogBaseUrl + "/resources/" + resourceId + "/tags",
324
+ HttpMethod.GET,
325
+ entity,
326
+ Map.class);
327
+
328
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
329
+ return (Map<String, String>) response.getBody().get("tags");
330
+ }
331
+ return Collections.emptyMap();
332
+ } catch (Exception e) {
333
+ logger.error("Error getting tags for resource: " + resourceId, e);
334
+ return Collections.emptyMap();
335
+ }
336
+ }
337
+
338
+ @Override
339
+ public boolean recordAccess(String resourceId, String userId, String accessType, Long timestamp) {
340
+ try {
341
+ Map<String, Object> requestBody = new HashMap<>();
342
+ requestBody.put("userId", userId);
343
+ requestBody.put("accessType", accessType);
344
+ if (timestamp != null) {
345
+ requestBody.put("timestamp", timestamp);
346
+ }
347
+
348
+ HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, createHeaders());
349
+ ResponseEntity<Map> response = restTemplate.exchange(
350
+ catalogBaseUrl + "/resources/" + resourceId + "/access",
351
+ HttpMethod.POST,
352
+ entity,
353
+ Map.class);
354
+
355
+ return response.getStatusCode().is2xxSuccessful();
356
+ } catch (Exception e) {
357
+ logger.error("Error recording access for resource: " + resourceId, e);
358
+ return false;
359
+ }
360
+ }
361
+
362
+ @Override
363
+ public List<Map<String, Object>> getAccessHistory(String resourceId, int limit) {
364
+ try {
365
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
366
+ ResponseEntity<Map> response = restTemplate.exchange(
367
+ catalogBaseUrl + "/resources/" + resourceId + "/access?limit=" + limit,
368
+ HttpMethod.GET,
369
+ entity,
370
+ Map.class);
371
+
372
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
373
+ return (List<Map<String, Object>>) response.getBody().get("accessRecords");
374
+ }
375
+ return Collections.emptyList();
376
+ } catch (Exception e) {
377
+ logger.error("Error getting access history for resource: " + resourceId, e);
378
+ return Collections.emptyList();
379
+ }
380
+ }
381
+
382
+ @Override
383
+ public String recordChange(ResourceChange change) {
384
+ try {
385
+ HttpEntity<ResourceChange> entity = new HttpEntity<>(change, createHeaders());
386
+ ResponseEntity<Map> response = restTemplate.exchange(
387
+ catalogBaseUrl + "/changes",
388
+ HttpMethod.POST,
389
+ entity,
390
+ Map.class);
391
+
392
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
393
+ return (String) response.getBody().get("changeId");
394
+ }
395
+ return null;
396
+ } catch (Exception e) {
397
+ logger.error("Error recording change", e);
398
+ return null;
399
+ }
400
+ }
401
+
402
+ @Override
403
+ public List<ResourceChange> getChangeHistory(String resourceId, int limit) {
404
+ try {
405
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
406
+ ResponseEntity<Map> response = restTemplate.exchange(
407
+ catalogBaseUrl + "/resources/" + resourceId + "/changes?limit=" + limit,
408
+ HttpMethod.GET,
409
+ entity,
410
+ Map.class);
411
+
412
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
413
+ List<Map<String, Object>> changes = (List<Map<String, Object>>) response.getBody().get("changes");
414
+ // This would need a proper mapper from Map to ResourceChange
415
+ return Collections.emptyList(); // Placeholder
416
+ }
417
+ return Collections.emptyList();
418
+ } catch (Exception e) {
419
+ logger.error("Error getting change history for resource: " + resourceId, e);
420
+ return Collections.emptyList();
421
+ }
422
+ }
423
+
424
+ @Override
425
+ public boolean isConnected() {
426
+ try {
427
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
428
+ ResponseEntity<Map> response = restTemplate.exchange(
429
+ catalogBaseUrl + "/health",
430
+ HttpMethod.GET,
431
+ entity,
432
+ Map.class);
433
+
434
+ return response.getStatusCode().is2xxSuccessful() &&
435
+ response.getBody() != null &&
436
+ "UP".equals(response.getBody().get("status"));
437
+ } catch (Exception e) {
438
+ logger.error("Error checking catalog connection", e);
439
+ return false;
440
+ }
441
+ }
442
+
443
+ @Override
444
+ public Map<String, Object> getCatalogInfo() {
445
+ try {
446
+ HttpEntity<Void> entity = new HttpEntity<>(createHeaders());
447
+ ResponseEntity<Map> response = restTemplate.exchange(
448
+ catalogBaseUrl + "/info",
449
+ HttpMethod.GET,
450
+ entity,
451
+ Map.class);
452
+
453
+ if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
454
+ return response.getBody();
455
+ }
456
+ return Collections.emptyMap();
457
+ } catch (Exception e) {
458
+ logger.error("Error getting catalog info", e);
459
+ return Collections.emptyMap();
460
+ }
461
+ }
462
+
463
+ @Override
464
+ public List<ResourceType> getAllResourceTypes() {
465
+ try {
466
+ HttpHeaders headers = createHeaders();
467
+ HttpEntity<String> entity = new HttpEntity<>(headers);
468
+
469
+ ResponseEntity<ResourceType[]> response = restTemplate.exchange(
470
+ catalogBaseUrl + "/resourceTypes",
471
+ HttpMethod.GET,
472
+ entity,
473
+ ResourceType[].class);
474
+
475
+ ResourceType[] resourceTypes = response.getBody();
476
+ if (resourceTypes != null) {
477
+ return List.of(resourceTypes);
478
+ }
479
+ } catch (RestClientException e) {
480
+ logger.error("Failed to get resource types from catalog: {}", e.getMessage(), e);
481
+ }
482
+ return Collections.emptyList();
483
+ }
484
+
485
+ @Override
486
+ public List<CloudService> getAllCloudServices() {
487
+ try {
488
+ HttpHeaders headers = createHeaders();
489
+ HttpEntity<String> entity = new HttpEntity<>(headers);
490
+
491
+ ResponseEntity<CloudService[]> response = restTemplate.exchange(
492
+ catalogBaseUrl + "/services",
493
+ HttpMethod.GET,
494
+ entity,
495
+ CloudService[].class);
496
+
497
+ CloudService[] services = response.getBody();
498
+ if (services != null) {
499
+ return List.of(services);
500
+ }
501
+ } catch (RestClientException e) {
502
+ logger.error("Failed to get cloud services from catalog: {}", e.getMessage(), e);
503
+ }
504
+ return Collections.emptyList();
505
+ }
506
+
507
+ @Override
508
+ public ResourceType getResourceType(String typeId) {
509
+ try {
510
+ HttpHeaders headers = createHeaders();
511
+ HttpEntity<String> entity = new HttpEntity<>(headers);
512
+
513
+ ResponseEntity<ResourceType> response = restTemplate.exchange(
514
+ catalogBaseUrl + "/resourceTypes/" + typeId,
515
+ HttpMethod.GET,
516
+ entity,
517
+ ResourceType.class);
518
+
519
+ return response.getBody();
520
+ } catch (RestClientException e) {
521
+ logger.error("Failed to get resource type {} from catalog: {}", typeId, e.getMessage(), e);
522
+ return null;
523
+ }
524
+ }
525
+
526
+ @Override
527
+ public CloudService getCloudService(String serviceId) {
528
+ try {
529
+ HttpHeaders headers = createHeaders();
530
+ HttpEntity<String> entity = new HttpEntity<>(headers);
531
+
532
+ ResponseEntity<CloudService> response = restTemplate.exchange(
533
+ catalogBaseUrl + "/services/" + serviceId,
534
+ HttpMethod.GET,
535
+ entity,
536
+ CloudService.class);
537
+
538
+ return response.getBody();
539
+ } catch (RestClientException e) {
540
+ logger.error("Failed to get cloud service {} from catalog: {}", serviceId, e.getMessage(), e);
541
+ return null;
542
+ }
543
+ }
544
+
545
+ @Override
546
+ public Map<String, Object> getServiceMappings() {
547
+ try {
548
+ HttpHeaders headers = createHeaders();
549
+ HttpEntity<String> entity = new HttpEntity<>(headers);
550
+
551
+ ResponseEntity<Map> response = restTemplate.exchange(
552
+ catalogBaseUrl + "/mappings",
553
+ HttpMethod.GET,
554
+ entity,
555
+ Map.class);
556
+
557
+ return response.getBody();
558
+ } catch (RestClientException e) {
559
+ logger.error("Failed to get service mappings from catalog: {}", e.getMessage(), e);
560
+ return Collections.emptyMap();
561
+ }
562
+ }
563
+ }
src/main/java/com/dalab/discovery/catalog/config/CatalogClientConfig.java ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.config;
2
+
3
+ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
4
+ import org.springframework.boot.context.properties.ConfigurationProperties;
5
+ import org.springframework.boot.context.properties.EnableConfigurationProperties;
6
+ import org.springframework.context.annotation.Bean;
7
+ import org.springframework.context.annotation.Configuration;
8
+ import org.springframework.web.client.RestTemplate;
9
+
10
+ import com.dalab.discovery.catalog.client.CatalogClient;
11
+ import com.dalab.discovery.catalog.client.impl.DefaultCatalogClient;
12
+
13
+ /**
14
+ * Configuration for the catalog client.
15
+ */
16
+ @Configuration
17
+ @EnableConfigurationProperties(CatalogClientConfig.CatalogProperties.class)
18
+ public class CatalogClientConfig {
19
+
20
+ /**
21
+ * Properties for the external catalog configuration.
22
+ */
23
+ @ConfigurationProperties(prefix = "catalog.external")
24
+ public static class CatalogProperties {
25
+ private boolean enabled = false;
26
+ private String baseUrl = "http://localhost:8080/api/catalog";
27
+ private String apiKey;
28
+ private int connectionTimeout = 5000;
29
+ private int readTimeout = 5000;
30
+
31
+ public boolean isEnabled() {
32
+ return enabled;
33
+ }
34
+
35
+ public void setEnabled(boolean enabled) {
36
+ this.enabled = enabled;
37
+ }
38
+
39
+ public String getBaseUrl() {
40
+ return baseUrl;
41
+ }
42
+
43
+ public void setBaseUrl(String baseUrl) {
44
+ this.baseUrl = baseUrl;
45
+ }
46
+
47
+ public String getApiKey() {
48
+ return apiKey;
49
+ }
50
+
51
+ public void setApiKey(String apiKey) {
52
+ this.apiKey = apiKey;
53
+ }
54
+
55
+ public int getConnectionTimeout() {
56
+ return connectionTimeout;
57
+ }
58
+
59
+ public void setConnectionTimeout(int connectionTimeout) {
60
+ this.connectionTimeout = connectionTimeout;
61
+ }
62
+
63
+ public int getReadTimeout() {
64
+ return readTimeout;
65
+ }
66
+
67
+ public void setReadTimeout(int readTimeout) {
68
+ this.readTimeout = readTimeout;
69
+ }
70
+ }
71
+
72
+ @Bean
73
+ @ConditionalOnProperty(name = "catalog.external.enabled", havingValue = "true")
74
+ public CatalogClient catalogClient(CatalogProperties properties) {
75
+ RestTemplate restTemplate = new RestTemplate();
76
+ return new DefaultCatalogClient(restTemplate, properties.getBaseUrl(), properties.getApiKey());
77
+ }
78
+ }
src/main/java/com/dalab/discovery/catalog/config/PersistenceConfig.java ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.config;
2
+
3
+ import java.util.Properties;
4
+
5
+ import javax.sql.DataSource;
6
+
7
+ import org.springframework.beans.factory.annotation.Autowired;
8
+ import org.springframework.boot.autoconfigure.domain.EntityScan;
9
+ import org.springframework.context.annotation.Bean;
10
+ import org.springframework.context.annotation.Configuration;
11
+ import org.springframework.context.annotation.Profile;
12
+ import org.springframework.core.env.Environment;
13
+ import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
14
+ import org.springframework.orm.jpa.JpaTransactionManager;
15
+ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
16
+ import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
17
+ import org.springframework.transaction.PlatformTransactionManager;
18
+ import org.springframework.transaction.annotation.EnableTransactionManagement;
19
+
20
+ /**
21
+ * Base configuration for persistence with Hibernate and Spring Data JPA.
22
+ */
23
+ @Configuration
24
+ @Profile("!dev")
25
+ @EnableTransactionManagement
26
+ @EnableJpaRepositories(basePackages = {
27
+ "com.dalab.discovery.catalog.model.repository",
28
+ "com.dalab.discovery.common.model.repository",
29
+ "com.dalab.discovery.crawler.model.repository",
30
+ "com.dalab.discovery.log.service.gcp.persistence.repository"
31
+ })
32
+ @EntityScan(basePackages = {
33
+ "com.dalab.discovery.catalog.model",
34
+ "com.dalab.discovery.common.model",
35
+ "com.dalab.discovery.crawler.model",
36
+ "com.dalab.discovery.log.service.gcp.persistence.entity"
37
+ })
38
+ public abstract class PersistenceConfig {
39
+
40
+ @Autowired
41
+ private Environment env;
42
+
43
+ /**
44
+ * Creates the entity manager factory.
45
+ *
46
+ * @param dataSource The data source
47
+ * @return The entity manager factory bean
48
+ */
49
+ @Bean
50
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
51
+ LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
52
+ em.setDataSource(dataSource);
53
+ em.setPackagesToScan(
54
+ "com.dalab.discovery.catalog.model",
55
+ "com.dalab.discovery.common.model",
56
+ "com.dalab.discovery.crawler.model");
57
+
58
+ HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
59
+ vendorAdapter.setGenerateDdl(false);
60
+ em.setJpaVendorAdapter(vendorAdapter);
61
+
62
+ em.setJpaProperties(additionalJpaProperties());
63
+
64
+ return em;
65
+ }
66
+
67
+ /**
68
+ * Creates the transaction manager.
69
+ *
70
+ * @param entityManagerFactory The entity manager factory
71
+ * @return The transaction manager
72
+ */
73
+ @Bean
74
+ public PlatformTransactionManager transactionManager(
75
+ LocalContainerEntityManagerFactoryBean entityManagerFactory) {
76
+ JpaTransactionManager transactionManager = new JpaTransactionManager();
77
+ transactionManager.setEntityManagerFactory(entityManagerFactory.getObject());
78
+ return transactionManager;
79
+ }
80
+
81
+ /**
82
+ * Configures additional JPA properties.
83
+ *
84
+ * @return The JPA properties
85
+ */
86
+ protected Properties additionalJpaProperties() {
87
+ Properties properties = new Properties();
88
+
89
+ // 1. DDL Auto Strategy
90
+ // Try the specific key from application-dev.yml, then standard Spring Boot key,
91
+ // then default to "update" (for tests).
92
+ String ddlAuto = env.getProperty("spring.jpa.properties.hibernate.hbm2ddl.auto");
93
+ if (ddlAuto == null) {
94
+ ddlAuto = env.getProperty("spring.jpa.hibernate.ddl-auto", "update");
95
+ }
96
+ properties.setProperty("hibernate.hbm2ddl.auto", ddlAuto);
97
+
98
+ // 2. Hibernate Dialect
99
+ // Try specific Hibernate dialect property, then standard Spring Boot
100
+ // database-platform, then default to H2 (for tests).
101
+ String dialect = env.getProperty("spring.jpa.properties.hibernate.dialect");
102
+ if (dialect == null) {
103
+ dialect = env.getProperty("spring.jpa.database-platform");
104
+ if (dialect == null) {
105
+ dialect = "org.hibernate.dialect.H2Dialect"; // Default for tests if no platform specified
106
+ }
107
+ }
108
+ properties.setProperty("hibernate.dialect", dialect);
109
+
110
+ // 3. Show SQL
111
+ // Read from standard Spring Boot key, default to "false".
112
+ properties.setProperty("hibernate.show_sql",
113
+ env.getProperty("spring.jpa.show-sql", "false"));
114
+
115
+ // 4. Format SQL
116
+ // Read from specific Hibernate property key, default to "false".
117
+ properties.setProperty("hibernate.format_sql",
118
+ env.getProperty("spring.jpa.properties.hibernate.format_sql", "false"));
119
+
120
+ // 5. Second Level Cache
121
+ properties.setProperty("hibernate.cache.use_second_level_cache",
122
+ env.getProperty("spring.jpa.properties.hibernate.cache.use_second_level_cache", "false"));
123
+
124
+ // 6. Query Cache
125
+ properties.setProperty("hibernate.cache.use_query_cache",
126
+ env.getProperty("spring.jpa.properties.hibernate.cache.use_query_cache", "false"));
127
+
128
+ // 7. JDBC Timezone
129
+ properties.setProperty("hibernate.jdbc.time_zone",
130
+ env.getProperty("spring.jpa.properties.hibernate.jdbc.time_zone", "UTC"));
131
+
132
+ return properties;
133
+ }
134
+ }
src/main/java/com/dalab/discovery/catalog/config/ProdStagingPersistenceConfig.java ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.config;
2
+
3
+ import org.springframework.beans.factory.annotation.Value;
4
+ import org.springframework.context.annotation.Configuration;
5
+ import org.springframework.context.annotation.Profile;
6
+
7
+ /**
8
+ * Production and staging environment persistence configuration using
9
+ * PostgreSQL.
10
+ */
11
+ @Configuration
12
+ @Profile({ "prod", "staging" })
13
+ public class ProdStagingPersistenceConfig extends PersistenceConfig {
14
+
15
+ @Value("${spring.datasource.url}")
16
+ private String url;
17
+
18
+ @Value("${spring.datasource.username}")
19
+ private String username;
20
+
21
+ @Value("${spring.datasource.password}")
22
+ private String password;
23
+
24
+ @Value("${spring.datasource.driver-class-name}")
25
+ private String driverClassName;
26
+ }
src/main/java/com/dalab/discovery/catalog/persistence/CloudHierarchyRegistry.java ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.persistence;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.Collections;
5
+ import java.util.HashMap;
6
+ import java.util.List;
7
+ import java.util.Map;
8
+ import java.util.Objects;
9
+ import java.util.Set;
10
+ import java.util.stream.Collectors;
11
+
12
+ import org.slf4j.Logger;
13
+ import org.slf4j.LoggerFactory;
14
+ import org.springframework.stereotype.Service;
15
+ import org.springframework.util.CollectionUtils;
16
+
17
+ import com.dalab.discovery.common.config.CloudHierarchyProperties;
18
+ import com.dalab.discovery.common.model.CloudService;
19
+ import com.dalab.discovery.common.model.ResourceType;
20
+ import com.dalab.discovery.common.model.enums.CloudProvider;
21
+
22
+ import jakarta.annotation.PostConstruct;
23
+
24
+ @Service
25
+ public class CloudHierarchyRegistry {
26
+
27
+ private static final Logger LOGGER = LoggerFactory.getLogger(CloudHierarchyRegistry.class);
28
+
29
+ private final CloudHierarchyProperties cloudHierarchyProperties;
30
+
31
+ // Internal storage for the loaded hierarchy (made unmodifiable after init)
32
+ private Map<String, CloudService> servicesById = Collections.emptyMap();
33
+ private Map<String, ResourceType> resourceTypesById = Collections.emptyMap();
34
+ private Map<CloudProvider, List<CloudService>> servicesByProvider = Collections.emptyMap();
35
+ private Map<CloudService, List<ResourceType>> resourceTypesByService = Collections.emptyMap();
36
+
37
+ public CloudHierarchyRegistry(CloudHierarchyProperties cloudHierarchyProperties) {
38
+ LOGGER.info("Initializing Cloud Hierarchy Registry with properties: {}", cloudHierarchyProperties);
39
+ this.cloudHierarchyProperties = Objects.requireNonNull(cloudHierarchyProperties,
40
+ "cloudHierarchyProperties cannot be null");
41
+ }
42
+
43
+ @PostConstruct
44
+ public void initialize() {
45
+ LOGGER.info("Initializing Cloud Hierarchy Registry...");
46
+ if (cloudHierarchyProperties == null || CollectionUtils.isEmpty(cloudHierarchyProperties.getProviders())) {
47
+ LOGGER.warn("Cloud hierarchy configuration is empty or null. Registry will be empty.");
48
+ // Keep maps empty
49
+ return;
50
+ }
51
+
52
+ Map<String, CloudService> tempServicesById = new HashMap<>();
53
+ Map<String, ResourceType> tempResourceTypesById = new HashMap<>();
54
+ Map<CloudProvider, List<CloudService>> tempServicesByProvider = new HashMap<>();
55
+ Map<CloudService, List<ResourceType>> tempResourceTypesByService = new HashMap<>();
56
+
57
+ try {
58
+ for (CloudHierarchyProperties.ProviderConfig providerConfig : cloudHierarchyProperties.getProviders()) {
59
+ CloudProvider currentProvider = providerConfig.getProvider();
60
+ if (currentProvider == null) {
61
+ throw new IllegalStateException("Configuration error: ProviderConfig missing 'provider' field.");
62
+ }
63
+ List<CloudService> providerServices = new ArrayList<>();
64
+ tempServicesByProvider.put(currentProvider, providerServices);
65
+
66
+ if (CollectionUtils.isEmpty(providerConfig.getServices())) {
67
+ LOGGER.warn("No services configured for provider: {}", currentProvider);
68
+ continue;
69
+ }
70
+
71
+ for (CloudHierarchyProperties.ServiceConfig serviceConfig : providerConfig.getServices()) {
72
+ // Create and validate Service
73
+ CloudService service = new CloudService(serviceConfig.getId(), serviceConfig.getDisplayName(),
74
+ currentProvider);
75
+ if (tempServicesById.containsKey(service.id())) {
76
+ throw new IllegalStateException("Duplicate service ID found in configuration: " + service.id());
77
+ }
78
+ tempServicesById.put(service.id(), service);
79
+ providerServices.add(service); // Add to the list for the current provider
80
+
81
+ List<ResourceType> serviceResourceTypes = new ArrayList<>();
82
+ tempResourceTypesByService.put(service, serviceResourceTypes);
83
+
84
+ if (CollectionUtils.isEmpty(serviceConfig.getResourceTypes())) {
85
+ LOGGER.warn("No resource types configured for service: {} ({})", service.id(),
86
+ service.displayName());
87
+ continue;
88
+ }
89
+
90
+ for (CloudHierarchyProperties.ResourceTypeConfig resourceTypeConfig : serviceConfig
91
+ .getResourceTypes()) {
92
+ // Create and validate ResourceType
93
+ ResourceType resourceType = new ResourceType(resourceTypeConfig.getId(),
94
+ resourceTypeConfig.getDisplayName(), service);
95
+ if (tempResourceTypesById.containsKey(resourceType.id())) {
96
+ throw new IllegalStateException(
97
+ "Duplicate resource type ID found in configuration: " + resourceType.id());
98
+ }
99
+ tempResourceTypesById.put(resourceType.id(), resourceType);
100
+ serviceResourceTypes.add(resourceType); // Add to the list for the current service
101
+ }
102
+ // Make the list for this service unmodifiable
103
+ tempResourceTypesByService.put(service,
104
+ Collections.unmodifiableList(new ArrayList<>(serviceResourceTypes)));
105
+ }
106
+ // Make the list for this provider unmodifiable
107
+ tempServicesByProvider.put(currentProvider,
108
+ Collections.unmodifiableList(new ArrayList<>(providerServices)));
109
+ }
110
+
111
+ // Make the main maps unmodifiable
112
+ this.servicesById = Collections.unmodifiableMap(tempServicesById);
113
+ this.resourceTypesById = Collections.unmodifiableMap(tempResourceTypesById);
114
+ this.servicesByProvider = Collections.unmodifiableMap(tempServicesByProvider);
115
+ this.resourceTypesByService = Collections.unmodifiableMap(tempResourceTypesByService);
116
+
117
+ LOGGER.info("Cloud Hierarchy Registry initialized successfully with {} services and {} resource types.",
118
+ this.servicesById.size(), this.resourceTypesById.size());
119
+
120
+ } catch (Exception e) {
121
+ LOGGER.error("Failed to initialize Cloud Hierarchy Registry from configuration: {}", e.getMessage(), e);
122
+ // Prevent partial initialization - keep maps empty
123
+ this.servicesById = Collections.emptyMap();
124
+ this.resourceTypesById = Collections.emptyMap();
125
+ this.servicesByProvider = Collections.emptyMap();
126
+ this.resourceTypesByService = Collections.emptyMap();
127
+ // Optionally re-throw as a runtime exception to halt application startup
128
+ throw new RuntimeException("Failed to initialize Cloud Hierarchy Registry", e);
129
+ }
130
+ }
131
+
132
+ // --- Public Accessor Methods ---
133
+
134
+ public CloudService getService(String serviceId) {
135
+ return servicesById.get(serviceId);
136
+ }
137
+
138
+ public ResourceType getResourceType(String resourceTypeId) {
139
+ return resourceTypesById.get(resourceTypeId);
140
+ }
141
+
142
+ public List<CloudService> getServices(CloudProvider provider) {
143
+ return servicesByProvider.getOrDefault(provider, Collections.emptyList());
144
+ }
145
+
146
+ public List<ResourceType> getResourceTypes(CloudService service) {
147
+ return resourceTypesByService.getOrDefault(service, Collections.emptyList());
148
+ }
149
+
150
+ public List<ResourceType> getResourceTypes(CloudProvider provider) {
151
+ return getServices(provider).stream()
152
+ .flatMap(service -> getResourceTypes(service).stream())
153
+ .collect(Collectors.toList()); // Already unmodifiable lists from getResourceTypes(Service)
154
+ }
155
+
156
+ public Set<CloudService> getAllServices() {
157
+ return Collections.unmodifiableSet(servicesById.values().stream().collect(Collectors.toSet()));
158
+ }
159
+
160
+ public Set<ResourceType> getAllResourceTypes() {
161
+ return Collections.unmodifiableSet(resourceTypesById.values().stream().collect(Collectors.toSet()));
162
+ }
163
+ }
src/main/java/com/dalab/discovery/catalog/persistence/IResourceCrawlerRegistry.java ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.persistence;
2
+
3
+ import java.util.Collection;
4
+ import java.util.List;
5
+
6
+ import com.dalab.discovery.crawler.service.IResourceCrawler;
7
+ import com.dalab.discovery.common.model.CloudResource;
8
+ import com.dalab.discovery.common.model.CloudService;
9
+ import com.dalab.discovery.common.model.ResourceType;
10
+ import com.dalab.discovery.common.model.enums.CloudProvider;
11
+
12
+ /**
13
+ * Registry for managing and retrieving resource crawlers.
14
+ */
15
+ public interface IResourceCrawlerRegistry {
16
+
17
+ /**
18
+ * Gets crawlers for a specific cloud provider.
19
+ *
20
+ * @param provider The cloud provider
21
+ * @return Collection of resource crawlers for the provider
22
+ */
23
+ Collection<IResourceCrawler<CloudResource>> getCrawlersForProvider(CloudProvider provider);
24
+
25
+ /**
26
+ * Gets crawlers for specific resource types and cloud provider.
27
+ *
28
+ * @param provider The cloud provider
29
+ * @param resourceTypes The resource types to get crawlers for
30
+ * @return Collection of resource crawlers for the specified types
31
+ */
32
+ Collection<IResourceCrawler<CloudResource>> getCrawlersForTypes(CloudProvider provider,
33
+ List<ResourceType> resourceTypes);
34
+
35
+ /**
36
+ * Registers a new resource crawler.
37
+ *
38
+ * @param resourceCrawler The crawler to register
39
+ * @return true if registration was successful
40
+ */
41
+ boolean registerCrawler(IResourceCrawler<?> resourceCrawler);
42
+
43
+ /**
44
+ * Unregisters an existing resource crawler.
45
+ *
46
+ * @param resourceCrawler The crawler to unregister
47
+ * @return true if unregistration was successful
48
+ */
49
+ boolean unregisterCrawler(IResourceCrawler<?> resourceCrawler);
50
+
51
+ /**
52
+ * Gets a crawler for a specific resource type.
53
+ *
54
+ * @param resourceType The resource type
55
+ * @return The crawler that can handle the type, or null if none found
56
+ */
57
+ IResourceCrawler<?> getCrawler(ResourceType resourceType);
58
+
59
+ /**
60
+ * Gets crawlers for a specific cloud service.
61
+ *
62
+ * @param service The cloud service
63
+ * @return List of resource crawlers for the service
64
+ */
65
+ List<IResourceCrawler<?>> getCrawlers(CloudService service);
66
+
67
+ /**
68
+ * Gets crawlers for a specific cloud provider (legacy method).
69
+ *
70
+ * @param provider The cloud provider
71
+ * @return List of resource crawlers for the provider
72
+ */
73
+ List<IResourceCrawler<?>> getCrawlers(CloudProvider provider);
74
+
75
+ /**
76
+ * Gets all registered crawlers.
77
+ *
78
+ * @return List of all resource crawlers
79
+ */
80
+ List<IResourceCrawler<?>> getAllCrawlers();
81
+ }
src/main/java/com/dalab/discovery/catalog/persistence/impl/DefaultResourceCrawlerRegistry.java ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.persistence.impl;
2
+
3
+ import java.util.Collection;
4
+ import java.util.Collections;
5
+ import java.util.List;
6
+ import java.util.Map;
7
+ import java.util.Objects;
8
+ import java.util.concurrent.ConcurrentHashMap;
9
+ import java.util.stream.Collectors;
10
+
11
+ import org.slf4j.Logger;
12
+ import org.slf4j.LoggerFactory;
13
+
14
+ import com.dalab.discovery.crawler.service.IResourceCrawler;
15
+ import com.dalab.discovery.common.model.CloudResource;
16
+ import com.dalab.discovery.common.model.CloudService;
17
+ // Import the new ResourceType and Service records
18
+ import com.dalab.discovery.common.model.ResourceType;
19
+ import com.dalab.discovery.common.model.enums.CloudProvider;
20
+ import com.dalab.discovery.catalog.persistence.CloudHierarchyRegistry;
21
+ import com.dalab.discovery.catalog.persistence.IResourceCrawlerRegistry;
22
+
23
+ /**
24
+ * Default implementation of ResourceRegistry that stores Crawlers in memory,
25
+ * mapped by the ResourceType they support.
26
+ */
27
+ @org.springframework.stereotype.Service // Fully qualified annotation
28
+ public class DefaultResourceCrawlerRegistry implements IResourceCrawlerRegistry {
29
+
30
+ private static final Logger LOGGER = LoggerFactory.getLogger(DefaultResourceCrawlerRegistry.class);
31
+
32
+ // Registry containing the loaded Service/ResourceType hierarchy
33
+ private final CloudHierarchyRegistry hierarchyRegistry;
34
+
35
+ // Maps ResourceType record to the corresponding Crawler that handles it.
36
+ // Assumes one crawler per resource type for simplicity. Use List<> if multiple
37
+ // are allowed.
38
+ private final Map<ResourceType, IResourceCrawler<?>> crawlersByResourceType = new ConcurrentHashMap<>();
39
+
40
+ // Constructor injection
41
+ public DefaultResourceCrawlerRegistry(CloudHierarchyRegistry hierarchyRegistry) {
42
+ this.hierarchyRegistry = Objects.requireNonNull(hierarchyRegistry, "hierarchyRegistry cannot be null");
43
+ }
44
+
45
+ @Override
46
+ public boolean registerCrawler(IResourceCrawler<?> resourceCrawler) {
47
+ if (resourceCrawler == null) {
48
+ LOGGER.warn("Attempted to register a null Crawler");
49
+ return false;
50
+ }
51
+
52
+ List<ResourceType> supportedTypes = resourceCrawler.getSupportedResourceTypes();
53
+ if (supportedTypes == null || supportedTypes.isEmpty()) {
54
+ LOGGER.warn("Crawler {} does not support any resource types. Skipping registration.",
55
+ resourceCrawler.getClass().getSimpleName());
56
+ return false;
57
+ }
58
+
59
+ LOGGER.info("Registering Crawler: {} for types: {}",
60
+ resourceCrawler.getClass().getSimpleName(),
61
+ supportedTypes.stream().map(ResourceType::id).collect(Collectors.joining(", ")));
62
+
63
+ boolean registeredAny = false;
64
+ for (ResourceType type : supportedTypes) {
65
+ IResourceCrawler<?> previous = crawlersByResourceType.put(type, resourceCrawler);
66
+ if (previous != null && previous != resourceCrawler) {
67
+ LOGGER.warn(
68
+ "Crawler registration conflict: ResourceType '{}' was handled by {} but is now handled by {}.",
69
+ type.id(), previous.getClass().getSimpleName(), resourceCrawler.getClass().getSimpleName());
70
+ }
71
+ registeredAny = true;
72
+ }
73
+ return registeredAny;
74
+ }
75
+
76
+ @Override
77
+ public boolean unregisterCrawler(IResourceCrawler<?> resourceCrawler) {
78
+ if (resourceCrawler == null) {
79
+ LOGGER.warn("Attempted to unregister a null Crawler");
80
+ return false;
81
+ }
82
+
83
+ LOGGER.info("Unregistering Crawler: {}", resourceCrawler.getClass().getSimpleName());
84
+
85
+ // Find all resource types handled by this crawler and remove them
86
+ // This is less efficient but necessary if a crawler handles multiple types.
87
+ // Consider a reverse map if performance becomes an issue.
88
+ List<ResourceType> keysToRemove = crawlersByResourceType.entrySet().stream()
89
+ .filter(entry -> entry.getValue() == resourceCrawler) // Use identity check
90
+ .map(Map.Entry::getKey)
91
+ .toList(); // Java 16+
92
+
93
+ if (keysToRemove.isEmpty()) {
94
+ LOGGER.warn("Crawler {} was not found in the registry.", resourceCrawler.getClass().getSimpleName());
95
+ return false;
96
+ }
97
+
98
+ keysToRemove.forEach(crawlersByResourceType::remove);
99
+ LOGGER.info("Unregistered crawler {} for types: {}",
100
+ resourceCrawler.getClass().getSimpleName(),
101
+ keysToRemove.stream().map(ResourceType::id).collect(Collectors.joining(", ")));
102
+ return true;
103
+ }
104
+
105
+ @Override
106
+ public IResourceCrawler<?> getCrawler(ResourceType resourceType) {
107
+ if (resourceType == null) {
108
+ return null;
109
+ }
110
+ return crawlersByResourceType.get(resourceType);
111
+ }
112
+
113
+ @Override
114
+ public List<IResourceCrawler<?>> getCrawlers(CloudService service) {
115
+ if (service == null) {
116
+ return Collections.emptyList();
117
+ }
118
+ // Find all resource types for this service from the hierarchy registry
119
+ return hierarchyRegistry.getResourceTypes(service).stream()
120
+ .map(this::getCrawler) // Look up the crawler for each type
121
+ .filter(Objects::nonNull) // Filter out types with no registered crawler
122
+ .distinct() // Ensure each crawler instance appears only once
123
+ .collect(Collectors.toUnmodifiableList());
124
+ }
125
+
126
+ @Override
127
+ public List<IResourceCrawler<?>> getCrawlers(CloudProvider provider) {
128
+ if (provider == null) {
129
+ return Collections.emptyList();
130
+ }
131
+ // Find all services for this provider, then get crawlers for each service
132
+ return hierarchyRegistry.getServices(provider).stream()
133
+ .flatMap(service -> getCrawlers(service).stream()) // Get crawlers for each service
134
+ .distinct() // Ensure each crawler instance appears only once
135
+ .collect(Collectors.toUnmodifiableList());
136
+ }
137
+
138
+ @Override
139
+ public Collection<IResourceCrawler<CloudResource>> getCrawlersForProvider(CloudProvider provider) {
140
+ if (provider == null) {
141
+ return Collections.emptyList();
142
+ }
143
+
144
+ // Cast to the required Collection type from our existing method
145
+ @SuppressWarnings("unchecked")
146
+ List<IResourceCrawler<CloudResource>> result = (List<IResourceCrawler<CloudResource>>) (List<?>) getCrawlers(
147
+ provider);
148
+ return result;
149
+ }
150
+
151
+ @Override
152
+ public Collection<IResourceCrawler<CloudResource>> getCrawlersForTypes(CloudProvider provider,
153
+ List<ResourceType> resourceTypes) {
154
+ if (provider == null || resourceTypes == null || resourceTypes.isEmpty()) {
155
+ return Collections.emptyList();
156
+ }
157
+
158
+ // Get all crawlers for this provider
159
+ Collection<IResourceCrawler<CloudResource>> allCrawlers = getCrawlersForProvider(provider);
160
+
161
+ // Filter to those that support any of the requested types
162
+ return allCrawlers.stream()
163
+ .filter(crawler -> crawler.getSupportedResourceTypes().stream()
164
+ .anyMatch(resourceTypes::contains))
165
+ .collect(Collectors.toList());
166
+ }
167
+
168
+ @Override
169
+ public List<IResourceCrawler<?>> getAllCrawlers() {
170
+ // Return a unique, unmodifiable list of all registered crawler instances
171
+ return crawlersByResourceType.values().stream()
172
+ .distinct()
173
+ .collect(Collectors.toUnmodifiableList());
174
+ }
175
+
176
+ // Remove the old getCrawlersByProvider method as getCrawlers(CloudProvider)
177
+ // replaces it
178
+ /*
179
+ * @Override
180
+ * public List<IResourceCrawler<?>> getCrawlersByProvider(CloudProvider
181
+ * cloudProvider) {
182
+ * // ... old implementation ...
183
+ * }
184
+ */
185
+ }
src/main/java/com/dalab/discovery/catalog/service/CatalogLiterals.java ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.service;
2
+
3
+ public class CatalogLiterals {
4
+
5
+ public static final String BASE_URL = "http://localhost:8080/api/2.1/unity-catalog"; // TODO Read it from config
6
+ public static final String CATALOGS_ENDPOINT = BASE_URL + "/catalogs";
7
+ public static final String SCHEMAS_ENDPOINT = BASE_URL + "/schemas";
8
+ public static final String TABLES_ENDPOINT = BASE_URL + "/tables";
9
+ public static final String CONTENT_TYPE = "Content-Type";
10
+ public static final String APPLICATION_JSON = "application/json";
11
+ public static final String ACCEPT = "Accept";
12
+ public static final String NAME = "name";
13
+ public static final String CATALOG_NAME = "catalog_name";
14
+ public static final String SCHEMA_NAME = "schema_name";
15
+ public static final String TABLE_TYPE = "table_type";
16
+ public static final String EXTERNAL = "EXTERNAL";
17
+ public static final String DATA_SOURCE_FORMAT = "data_source_format";
18
+ public static final String DELTA = "DELTA";
19
+ public static final String COLUMNS = "columns";
20
+ public static final String STORAGE_LOCATION = "storage_location";
21
+ public static final String COMMENT = "comment";
22
+ public static final String PROPERTIES = "properties";
23
+ public static final String ADDITIONAL_PROP1 = "additionalProp1";
24
+ public static final String ADDITIONAL_PROP2 = "additionalProp2";
25
+ public static final String ADDITIONAL_PROP3 = "additionalProp3";
26
+ public static final String VALUE1 = "value1";
27
+ public static final String VALUE2 = "value2";
28
+ public static final String VALUE3 = "value3";
29
+ public static final String ID = "id";
30
+ public static final String INT = "INT";
31
+ public static final String STRING = "STRING";
32
+ public static final String PRIMARY_KEY_COLUMN = "Primary key column";
33
+ public static final String NAME_OF_ENTITY = "Name of the entity";
34
+ public static final String POSITION = "position";
35
+ public static final String TYPE_TEXT = "type_text";
36
+ public static final String TYPE_JSON = "type_json";
37
+ public static final String TYPE_NAME = "type_name";
38
+ public static final String TYPE_PRECISION = "type_precision";
39
+ public static final String TYPE_SCALE = "type_scale";
40
+ public static final String TYPE_INTERVAL_TYPE = "type_interval_type";
41
+ public static final String NULLABLE = "nullable";
42
+ public static final String PARTITION_INDEX = "partition_index";
43
+ }
src/main/java/com/dalab/discovery/catalog/service/ICatalogService.java ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.service;
2
+
3
+ import java.time.Instant;
4
+ import java.util.List;
5
+ import java.util.Map;
6
+ import java.util.Optional;
7
+ import java.util.UUID;
8
+
9
+ import org.springframework.data.domain.Page;
10
+ import org.springframework.data.domain.Pageable;
11
+
12
+ import com.dalab.discovery.common.model.CloudResource;
13
+ import com.dalab.discovery.common.model.ResourceChange;
14
+ import com.dalab.discovery.common.model.ResourceType;
15
+ import com.dalab.discovery.common.model.enums.CloudProvider;
16
+
17
+ /**
18
+ * Consolidated service interface for catalog operations.
19
+ * Works with the com.dalab.discovery.common.models (CloudResource,
20
+ * ResourceChange).
21
+ */
22
+ public interface ICatalogService {
23
+
24
+ // --- Resource Query Methods ---
25
+
26
+ /**
27
+ * Retrieves a resource by its internal database primary key.
28
+ *
29
+ * @param resourceDbId The database UUID.
30
+ * @return Optional containing the resource if found.
31
+ */
32
+ Optional<CloudResource> getResource(UUID resourceDbId);
33
+
34
+ /**
35
+ * Retrieves a resource by its business key (resource ID, provider, type ID).
36
+ *
37
+ * @param resourceId The provider-specific resource ID.
38
+ * @param provider The cloud provider.
39
+ * @param typeId The resource type ID.
40
+ * @return Optional containing the resource if found.
41
+ */
42
+ Optional<CloudResource> getResourceByKey(String resourceId, CloudProvider provider, String typeId);
43
+
44
+ /**
45
+ * Retrieves resources by type (using ResourceType record) and provider.
46
+ * Provides pagination support.
47
+ *
48
+ * @param type The ResourceType record.
49
+ * @param provider The cloud provider.
50
+ * @param pageable Pagination information.
51
+ * @return A Page of matching resources.
52
+ */
53
+ Page<CloudResource> getResourcesByType(ResourceType type, CloudProvider provider, Pageable pageable);
54
+
55
+ /**
56
+ * Retrieves resources by Account ID (e.g., AWS Account, Azure Subscription, GCP
57
+ * Project).
58
+ * Provides pagination support.
59
+ *
60
+ * @param accountId The account identifier.
61
+ * @param pageable Pagination information.
62
+ * @return A Page of resources associated with the account.
63
+ */
64
+ Page<CloudResource> getResourcesByAccount(String accountId, Pageable pageable);
65
+
66
+ // --- Change Processing Method ---
67
+
68
+ /**
69
+ * Updates resources in the catalog.
70
+ *
71
+ * @param resources The resources to update
72
+ */
73
+ void updateResources(List<CloudResource> resources);
74
+
75
+ /**
76
+ * Processes a resource change.
77
+ *
78
+ * @param change The resource change to process
79
+ */
80
+ void processResourceChange(ResourceChange change);
81
+
82
+ /**
83
+ * Processes multiple resource changes.
84
+ *
85
+ * @param changes The resource changes to process
86
+ */
87
+ default void processResourceChanges(List<ResourceChange> changes) {
88
+ if (changes != null) {
89
+ changes.forEach(this::processResourceChange);
90
+ }
91
+ }
92
+
93
+ // --- Change History Query Methods ---
94
+
95
+ /**
96
+ * Retrieves change history for a specific resource (identified by DB ID).
97
+ * Provides pagination support.
98
+ *
99
+ * @param resourceDbId The database UUID of the CloudResource.
100
+ * @param pageable Pagination information.
101
+ * @return A Page of ResourceChange records for the resource.
102
+ */
103
+ Page<ResourceChange> getResourceChanges(UUID resourceDbId, Pageable pageable);
104
+
105
+ /**
106
+ * Retrieves change history within a specific time range.
107
+ * Provides pagination support.
108
+ *
109
+ * @param start Start time (inclusive).
110
+ * @param end End time (exclusive).
111
+ * @param pageable Pagination information.
112
+ * @return A Page of ResourceChange records within the time range.
113
+ */
114
+ Page<ResourceChange> getChangesByTimeRange(Instant start, Instant end, Pageable pageable);
115
+
116
+ // --- Potentially useful methods adapted from ICatalogUpdater ---
117
+ // These might need adjustment or implementation based on requirements
118
+
119
+ /**
120
+ * Updates tags for a specific resource.
121
+ *
122
+ * @param resourceDbId The database UUID of the CloudResource.
123
+ * @param tags The complete set of tags to apply.
124
+ * @return The updated CloudResource, or empty if not found.
125
+ */
126
+ Optional<CloudResource> updateResourceTags(UUID resourceDbId, Map<String, String> tags);
127
+
128
+ /**
129
+ * Updates technical metadata for a specific resource.
130
+ *
131
+ * @param resourceDbId The database UUID of the CloudResource.
132
+ * @param metadata The complete set of technical metadata to apply.
133
+ * @return The updated CloudResource, or empty if not found.
134
+ */
135
+ Optional<CloudResource> updateTechnicalMetadata(UUID resourceDbId, Map<String, String> metadata);
136
+
137
+ /**
138
+ * Updates business metadata for a specific resource.
139
+ *
140
+ * @param resourceDbId The database UUID of the CloudResource.
141
+ * @param metadata The complete set of business metadata to apply.
142
+ * @return The updated CloudResource, or empty if not found.
143
+ */
144
+ Optional<CloudResource> updateBusinessMetadata(UUID resourceDbId, Map<String, String> metadata);
145
+
146
+ // markResourceDeleted might be handled by processResourceChanges(DELETE)
147
+ // updateResourceProperties merged into metadata methods?
148
+ }
src/main/java/com/dalab/discovery/catalog/service/UnityCatalogManager.java ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.service;
2
+
3
+ import java.io.BufferedReader;
4
+ import java.io.InputStreamReader;
5
+ import java.io.OutputStream;
6
+ import java.net.HttpURLConnection;
7
+ import java.net.URL;
8
+ import java.nio.charset.StandardCharsets;
9
+ import org.slf4j.Logger;
10
+ import org.slf4j.LoggerFactory;
11
+
12
+ public class UnityCatalogManager {
13
+
14
+ private static final Logger log = LoggerFactory.getLogger(UnityCatalogManager.class);
15
+
16
+ public void createCatalog(String catalogName) throws Exception {
17
+ String payload = "{\"" + CatalogLiterals.NAME + "\": \"" + catalogName + "\"}";
18
+ log.info("Creating catalog: " + catalogName);
19
+ ApiResponse response = sendPostRequest(CatalogLiterals.CATALOGS_ENDPOINT, payload);
20
+ handleResponse(response, "Catalog");
21
+ }
22
+
23
+ public void createSchema(String catalogName, String schemaName) throws Exception {
24
+ String payload =
25
+ "{\"" + CatalogLiterals.NAME + "\": \"" + schemaName + "\", \"" + CatalogLiterals.CATALOG_NAME + "\": \"" + catalogName + "\"}";
26
+ log.info("Creating schema: " + schemaName + " in catalog: " + catalogName);
27
+ ApiResponse response = sendPostRequest(CatalogLiterals.SCHEMAS_ENDPOINT, payload);
28
+ handleResponse(response, "Schema");
29
+ }
30
+
31
+
32
+ static ApiResponse sendPostRequest(String endpoint, String payload) throws Exception {
33
+ HttpURLConnection conn = null;
34
+ try {
35
+ URL url = new URL(endpoint);
36
+ conn = (HttpURLConnection) url.openConnection();
37
+ conn.setRequestMethod("POST");
38
+ conn.setRequestProperty("Content-Type", "application/json");
39
+ conn.setRequestProperty("Accept", "application/json");
40
+ conn.setDoOutput(true);
41
+
42
+ // Write request payload
43
+ try (OutputStream os = conn.getOutputStream()) {
44
+ byte[] input = payload.getBytes(StandardCharsets.UTF_8);
45
+ os.write(input, 0, input.length);
46
+ }
47
+
48
+ // Read response
49
+ int responseCode = conn.getResponseCode();
50
+ String responseBody;
51
+ try (
52
+ BufferedReader br = new BufferedReader(
53
+ new InputStreamReader(responseCode < 400 ? conn.getInputStream() : conn.getErrorStream(), StandardCharsets.UTF_8)
54
+ )
55
+ ) {
56
+ StringBuilder response = new StringBuilder();
57
+ String responseLine;
58
+ while ((responseLine = br.readLine()) != null) {
59
+ response.append(responseLine.trim());
60
+ }
61
+ responseBody = response.toString();
62
+ }
63
+
64
+ return new ApiResponse(responseCode, responseBody);
65
+ } finally {
66
+ if (conn != null) {
67
+ conn.disconnect();
68
+ }
69
+ }
70
+ }
71
+
72
+ void handleResponse(ApiResponse response, String resourceType) {
73
+ if (response.getStatusCode() == HttpURLConnection.HTTP_OK || response.getStatusCode() == HttpURLConnection.HTTP_CREATED) {
74
+ log.info(resourceType + " created successfully. Response: " + response.getResponseBody());
75
+ } else {
76
+ log.info(
77
+ "Failed to create " + resourceType + ". Status Code: " + response.getStatusCode() + ", Error: " + response.getResponseBody()
78
+ );
79
+ throw new RuntimeException("Failed to create " + resourceType + ": " + response.getResponseBody());
80
+ }
81
+ }
82
+
83
+ // Helper class to store API response
84
+ static class ApiResponse {
85
+
86
+ private final int statusCode;
87
+ private final String responseBody;
88
+
89
+ public ApiResponse(int statusCode, String responseBody) {
90
+ this.statusCode = statusCode;
91
+ this.responseBody = responseBody;
92
+ }
93
+
94
+ public int getStatusCode() {
95
+ return statusCode;
96
+ }
97
+
98
+ public String getResponseBody() {
99
+ return responseBody;
100
+ }
101
+ }
102
+ }
src/main/java/com/dalab/discovery/catalog/service/impl/CatalogServiceImpl.java ADDED
@@ -0,0 +1,501 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.catalog.service.impl;
2
+
3
+ import java.time.Instant;
4
+ import java.time.temporal.ChronoUnit;
5
+ import java.util.List;
6
+ import java.util.Map;
7
+ import java.util.Optional;
8
+ import java.util.UUID;
9
+
10
+ import org.slf4j.Logger;
11
+ import org.slf4j.LoggerFactory;
12
+ import org.springframework.beans.factory.annotation.Autowired;
13
+ import org.springframework.data.domain.Page;
14
+ import org.springframework.data.domain.Pageable;
15
+ import org.springframework.stereotype.Service;
16
+ import org.springframework.transaction.annotation.Transactional;
17
+
18
+ import com.dalab.discovery.catalog.service.ICatalogService;
19
+ import com.dalab.discovery.common.model.CloudResource;
20
+ import com.dalab.discovery.common.model.CloudResourceFactory;
21
+ import com.dalab.discovery.common.model.CloudResourceUsageStats;
22
+ import com.dalab.discovery.common.model.ResourceChange;
23
+ import com.dalab.discovery.common.model.ResourceType;
24
+ import com.dalab.discovery.common.model.enums.CloudProvider;
25
+ import com.dalab.discovery.common.model.repository.CloudResourceRepository;
26
+ import com.dalab.discovery.common.model.repository.CloudResourceUsageStatsRepository;
27
+ import com.dalab.discovery.common.model.repository.ResourceChangeRepository;
28
+
29
+ /**
30
+ * Implementation of the consolidated ICatalogService.
31
+ * Handles querying and updating the catalog based on the sd.domain model.
32
+ */
33
+ @Service
34
+ @Transactional
35
+ public class CatalogServiceImpl implements ICatalogService {
36
+
37
+ private static final Logger log = LoggerFactory.getLogger(CatalogServiceImpl.class);
38
+
39
+ private final CloudResourceRepository cloudResourceRepository;
40
+ private final ResourceChangeRepository resourceChangeRepository;
41
+ private final CloudResourceUsageStatsRepository usageStatsRepository;
42
+ private final CloudResourceFactory cloudResourceFactory;
43
+
44
+ @Autowired
45
+ public CatalogServiceImpl(CloudResourceRepository cloudResourceRepository,
46
+ ResourceChangeRepository resourceChangeRepository,
47
+ CloudResourceUsageStatsRepository usageStatsRepository,
48
+ CloudResourceFactory cloudResourceFactory) {
49
+ this.cloudResourceRepository = cloudResourceRepository;
50
+ this.resourceChangeRepository = resourceChangeRepository;
51
+ this.usageStatsRepository = usageStatsRepository;
52
+ this.cloudResourceFactory = cloudResourceFactory;
53
+ }
54
+
55
+ @Override
56
+ @Transactional(readOnly = true)
57
+ public Optional<CloudResource> getResource(UUID resourceDbId) {
58
+ log.debug("Fetching resource by DB ID: {}", resourceDbId);
59
+ return cloudResourceRepository.findById(resourceDbId);
60
+ }
61
+
62
+ @Override
63
+ @Transactional(readOnly = true)
64
+ public Optional<CloudResource> getResourceByKey(String resourceId, CloudProvider provider, String typeId) {
65
+ log.debug("Fetching resource by key: resourceId={}, provider={}, typeId={}", resourceId, provider, typeId);
66
+ return cloudResourceRepository.findByResourceIdAndCloudProviderAndTypeId(resourceId, provider, typeId);
67
+ }
68
+
69
+ @Override
70
+ @Transactional(readOnly = true)
71
+ public Page<CloudResource> getResourcesByType(ResourceType type, CloudProvider provider, Pageable pageable) {
72
+ if (type == null || provider == null || type.service() == null) {
73
+ log.warn("ResourceType, Provider, or Service within ResourceType is null, cannot query by type.");
74
+ return Page.empty(pageable);
75
+ }
76
+ log.debug("Fetching resources by type: provider={}, serviceId={}, typeId={}, pageable={}",
77
+ provider, type.service().id(), type.id(), pageable);
78
+
79
+ return cloudResourceRepository.findByCloudProviderAndServiceIdAndTypeId(
80
+ provider, type.service().id(), type.id(), pageable);
81
+ }
82
+
83
+ @Override
84
+ @Transactional(readOnly = true)
85
+ public Page<CloudResource> getResourcesByAccount(String accountId, Pageable pageable) {
86
+ log.debug("Fetching resources by account: {}, pageable={}", accountId, pageable);
87
+
88
+ return cloudResourceRepository.findByAccountId(accountId, pageable);
89
+ }
90
+
91
+ @Override
92
+ public void processResourceChanges(List<ResourceChange> changes) {
93
+ if (changes == null || changes.isEmpty()) {
94
+ log.debug("No resource changes to process.");
95
+ return;
96
+ }
97
+ log.info("Processing {} resource change(s)...", changes.size());
98
+
99
+ for (ResourceChange change : changes) {
100
+ try {
101
+ processResourceChange(change);
102
+ } catch (Exception e) {
103
+ log.error("Failed to process resource change for resourceId: {} - {}",
104
+ change.getResourceId(), e.getMessage(), e);
105
+ }
106
+ }
107
+ log.info("Finished processing resource changes.");
108
+ }
109
+
110
+ @Override
111
+ public void processResourceChange(ResourceChange change) {
112
+ if (change == null) {
113
+ log.debug("No resource change to process.");
114
+ return;
115
+ }
116
+
117
+ try {
118
+ resourceChangeRepository.save(change);
119
+ Optional<CloudResource> existingResourceOpt = cloudResourceRepository
120
+ .findByResourceIdAndCloudProviderAndTypeId(
121
+ change.getResourceId(),
122
+ change.getCloudProvider(),
123
+ change.getTypeId());
124
+
125
+ switch (change.getChangeType()) {
126
+ case CREATE:
127
+ handleCreate(change, existingResourceOpt);
128
+ break;
129
+ case UPDATE:
130
+ handleUpdate(change, existingResourceOpt);
131
+ break;
132
+ case DELETE:
133
+ handleDelete(change, existingResourceOpt);
134
+ break;
135
+ case ACCESS:
136
+ handleAccess(change, existingResourceOpt);
137
+ break;
138
+ case PERMISSION:
139
+ handlePermission(change, existingResourceOpt);
140
+ break;
141
+ default:
142
+ handleUnknown(change, existingResourceOpt);
143
+ break;
144
+ }
145
+ } catch (Exception e) {
146
+ log.error("Failed to process resource change for resourceId: {} - {}",
147
+ change.getResourceId(), e.getMessage(), e);
148
+ throw e; // Re-throw to be consistent with interface expectations
149
+ }
150
+ }
151
+
152
+ @Override
153
+ @Transactional(readOnly = true)
154
+ public Page<ResourceChange> getResourceChanges(UUID resourceDbId, Pageable pageable) {
155
+ log.debug("Fetching changes for resource DB ID: {}, pageable={}", resourceDbId, pageable);
156
+
157
+ // Get the resource first to determine its business key
158
+ return cloudResourceRepository.findById(resourceDbId)
159
+ .map(resource -> resourceChangeRepository.findByResourceIdOrderByTimestampDesc(
160
+ resource.getResourceId(), pageable))
161
+ .orElse(Page.empty(pageable));
162
+ }
163
+
164
+ @Override
165
+ @Transactional(readOnly = true)
166
+ public Page<ResourceChange> getChangesByTimeRange(Instant start, Instant end, Pageable pageable) {
167
+ log.debug("Fetching changes between {} and {}, pageable={}", start, end, pageable);
168
+
169
+ return resourceChangeRepository.findByTimestampBetweenOrderByTimestampDesc(start, end, pageable);
170
+ }
171
+
172
+ @Override
173
+ public Optional<CloudResource> updateResourceTags(UUID resourceDbId, Map<String, String> tags) {
174
+ log.info("Updating tags for resource DB ID: {}", resourceDbId);
175
+ return cloudResourceRepository.findById(resourceDbId).map(resource -> {
176
+ resource.setTags(tags);
177
+ resource.setUpdatedAt(Instant.now());
178
+ // Track change? Maybe handled by other flows.
179
+ return cloudResourceRepository.save(resource);
180
+ });
181
+ }
182
+
183
+ @Override
184
+ public Optional<CloudResource> updateTechnicalMetadata(UUID resourceDbId, Map<String, String> metadata) {
185
+ log.info("Updating technical metadata for resource DB ID: {}", resourceDbId);
186
+ return cloudResourceRepository.findById(resourceDbId).map(resource -> {
187
+ resource.setTechnicalMetadata(metadata);
188
+ resource.setUpdatedAt(Instant.now());
189
+ return cloudResourceRepository.save(resource);
190
+ });
191
+ }
192
+
193
+ @Override
194
+ public Optional<CloudResource> updateBusinessMetadata(UUID resourceDbId, Map<String, String> metadata) {
195
+ log.info("Updating business metadata for resource DB ID: {}", resourceDbId);
196
+ return cloudResourceRepository.findById(resourceDbId).map(resource -> {
197
+ resource.setBusinessMetadata(metadata);
198
+ resource.setUpdatedAt(Instant.now());
199
+ return cloudResourceRepository.save(resource);
200
+ });
201
+ }
202
+
203
+ @Override
204
+ public void updateResources(List<CloudResource> resources) {
205
+ if (resources == null || resources.isEmpty()) {
206
+ log.debug("No resources to update.");
207
+ return;
208
+ }
209
+
210
+ log.info("Updating {} resources in catalog...", resources.size());
211
+
212
+ for (CloudResource resource : resources) {
213
+ try {
214
+ // Check if resource already exists
215
+ Optional<CloudResource> existingResource = cloudResourceRepository
216
+ .findByResourceIdAndCloudProviderAndTypeId(
217
+ resource.getResourceId(),
218
+ resource.getCloudProviderEnum(),
219
+ resource.getTypeId());
220
+
221
+ if (existingResource.isPresent()) {
222
+ CloudResource current = existingResource.get();
223
+
224
+ // Update fields that should be preserved
225
+ current.setName(resource.getName());
226
+ current.setRegion(resource.getRegion());
227
+ current.setLocation(resource.getLocation());
228
+ current.setParentId(resource.getParentId());
229
+ current.setUri(resource.getUri());
230
+ current.setDescription(resource.getDescription());
231
+ current.setTags(resource.getTags());
232
+ current.setTechnicalMetadata(resource.getTechnicalMetadata());
233
+ current.setBusinessMetadata(resource.getBusinessMetadata());
234
+ current.setLastDiscoveredAt(Instant.now());
235
+ current.setUpdatedAt(Instant.now());
236
+
237
+ cloudResourceRepository.save(current);
238
+ log.debug("Updated existing resource: {} ({})", current.getId(), current.getResourceId());
239
+ } else {
240
+ // Set creation timestamps for new resources
241
+ resource.setCreatedAt(Instant.now());
242
+ resource.setUpdatedAt(Instant.now());
243
+ resource.setLastDiscoveredAt(Instant.now());
244
+
245
+ cloudResourceRepository.save(resource);
246
+ log.debug("Added new resource: {}", resource.getResourceId());
247
+ }
248
+ } catch (Exception e) {
249
+ log.error("Error updating resource {}: {}", resource.getResourceId(), e.getMessage(), e);
250
+ }
251
+ }
252
+
253
+ log.info("Finished updating resources in catalog.");
254
+ }
255
+
256
+ private void handleCreate(ResourceChange change, Optional<CloudResource> existingResourceOpt) {
257
+ if (existingResourceOpt.isPresent()) {
258
+ log.warn("CREATE event for existing resource: {}. Treating as UPDATE.", change.getResourceId());
259
+ handleUpdate(change, existingResourceOpt);
260
+ } else {
261
+ log.info("Processing CREATE for resource: {}", change.getResourceId());
262
+
263
+ // Create appropriate CloudResource subclass using factory
264
+ CloudResource newResource = cloudResourceFactory.createResource(
265
+ change.getCloudProvider(),
266
+ change.getResourceId(),
267
+ change.getServiceId(),
268
+ change.getTypeId(),
269
+ change.getDetails().getOrDefault(
270
+ change.getCloudProvider().name().toLowerCase() + ".resourceNameShort",
271
+ change.getResourceId()));
272
+
273
+ if (newResource == null) {
274
+ log.error("Failed to create resource instance for provider: {}", change.getCloudProvider());
275
+ return;
276
+ }
277
+
278
+ // Set Account ID (example assumes GCP uses projectId, generalize for others)
279
+ if (change.getCloudProvider() == CloudProvider.GCP) {
280
+ newResource.setAccountId(change.getProjectId());
281
+ } else {
282
+ // Extract account ID for other providers from change details or context
283
+ newResource.setAccountId(change.getDetails().get("accountId")); // Example placeholder
284
+ }
285
+
286
+ newResource.setCreatedAt(change.getTimestamp());
287
+ newResource.setUpdatedAt(change.getTimestamp());
288
+ newResource.setLastDiscoveredAt(Instant.now());
289
+
290
+ // Extract region etc. from details map
291
+ newResource.setRegion(
292
+ change.getDetails().get(change.getCloudProvider().name().toLowerCase() + ".request.region"));
293
+ newResource.setLocation(
294
+ change.getDetails().get(change.getCloudProvider().name().toLowerCase() + ".request.location"));
295
+ // ... populate other common fields like description, uri, parentId if available
296
+ // in details
297
+
298
+ mapMetadata(newResource, change.getDetails());
299
+
300
+ // Create and link initial usage stats
301
+ Instant windowStart = Instant.now().truncatedTo(ChronoUnit.DAYS);
302
+ Instant windowEnd = windowStart.plus(30, ChronoUnit.DAYS); // Example: 30 day window
303
+ CloudResourceUsageStats stats = new CloudResourceUsageStats(newResource, windowStart, windowEnd);
304
+ stats.setLastAccessedAt(change.getTimestamp());
305
+ stats.incrementEditCount(); // Treat creation as 1 edit/write
306
+ newResource.setUsageStats(stats); // Link stats via the setter
307
+
308
+ cloudResourceRepository.save(newResource); // Cascade should save stats
309
+ log.info("Created resource: {} ({})", newResource.getId(), newResource.getResourceId());
310
+ }
311
+ }
312
+
313
+ private void handleUpdate(ResourceChange change, Optional<CloudResource> existingResourceOpt) {
314
+ existingResourceOpt.ifPresentOrElse(resource -> {
315
+ log.info("Processing UPDATE for resource: {} ({})", resource.getId(), resource.getResourceId());
316
+ resource.setUpdatedAt(change.getTimestamp());
317
+ resource.setLastDiscoveredAt(Instant.now());
318
+ // Optionally update name, region, etc. based on details
319
+ String nameKey = change.getCloudProvider().name().toLowerCase() + ".resourceNameShort";
320
+ if (change.getDetails().containsKey(nameKey)) {
321
+ resource.setName(change.getDetails().get(nameKey));
322
+ }
323
+ // ... other field updates
324
+ mapMetadata(resource, change.getDetails());
325
+ updateUsageStats(resource, ResourceChange.ChangeType.UPDATE, change.getTimestamp());
326
+ cloudResourceRepository.save(resource);
327
+ log.debug("Updated resource: {} ({})", resource.getId(), resource.getResourceId());
328
+ }, () -> log.warn("UPDATE event for non-existent resource: {}. Ignoring.", change.getResourceId()));
329
+ }
330
+
331
+ private void handleDelete(ResourceChange change, Optional<CloudResource> existingResourceOpt) {
332
+ existingResourceOpt.ifPresentOrElse(resource -> {
333
+ UUID resourceDbId = resource.getId();
334
+ String resourceId = resource.getResourceId();
335
+ log.info("Processing DELETE for resource: {} ({})", resourceDbId, resourceId);
336
+ // Delete associated stats first (if relation isn't cascading delete properly)
337
+ CloudResourceUsageStats stats = resource.getUsageStats();
338
+ if (stats != null) {
339
+ try {
340
+ usageStatsRepository.deleteById(stats.getId());
341
+ } catch (Exception e) {
342
+ log.error("Error deleting usage stats for resource {}: {}", resourceDbId, e.getMessage());
343
+ }
344
+ }
345
+ cloudResourceRepository.delete(resource);
346
+ log.info("Deleted resource: {} ({})", resourceDbId, resourceId);
347
+ }, () -> log.warn("DELETE event for non-existent resource: {}. Ignoring.", change.getResourceId()));
348
+ }
349
+
350
+ private void handleAccess(ResourceChange change, Optional<CloudResource> existingResourceOpt) {
351
+ existingResourceOpt.ifPresentOrElse(resource -> {
352
+ log.info("Processing ACCESS for resource: {} ({})", resource.getId(), resource.getResourceId());
353
+ updateUsageStats(resource, ResourceChange.ChangeType.ACCESS, change.getTimestamp());
354
+ }, () -> log.warn("ACCESS event for non-existent resource: {}. Ignoring.", change.getResourceId()));
355
+ }
356
+
357
+ private void handlePermission(ResourceChange change, Optional<CloudResource> existingResourceOpt) {
358
+ existingResourceOpt.ifPresentOrElse(resource -> {
359
+ log.info("Processing PERMISSION for resource: {} ({})", resource.getId(), resource.getResourceId());
360
+ resource.setUpdatedAt(change.getTimestamp());
361
+ resource.setLastDiscoveredAt(Instant.now());
362
+ mapMetadata(resource, change.getDetails());
363
+ updateUsageStats(resource, ResourceChange.ChangeType.PERMISSION, change.getTimestamp()); // Count as edit
364
+ cloudResourceRepository.save(resource);
365
+ log.debug("Updated resource due to PERMISSION change: {} ({})", resource.getId(), resource.getResourceId());
366
+ }, () -> log.warn("PERMISSION event for non-existent resource: {}. Ignoring.", change.getResourceId()));
367
+ }
368
+
369
+ private void handleUnknown(ResourceChange change, Optional<CloudResource> existingResourceOpt) {
370
+ existingResourceOpt.ifPresentOrElse(resource -> {
371
+ log.info("Processing UNKNOWN change for resource: {} ({}) as potential update.", resource.getId(),
372
+ resource.getResourceId());
373
+ resource.setUpdatedAt(change.getTimestamp());
374
+ resource.setLastDiscoveredAt(Instant.now());
375
+ mapMetadata(resource, change.getDetails());
376
+ cloudResourceRepository.save(resource);
377
+ // Optionally update usage stats?
378
+ }, () -> log.warn("UNKNOWN event for non-existent resource: {}. Ignoring.", change.getResourceId()));
379
+ }
380
+
381
+ private void updateUsageStats(CloudResource resource, ResourceChange.ChangeType changeType, Instant eventTime) {
382
+ CloudResourceUsageStats stats = resource.getUsageStats();
383
+ boolean needsSave = false;
384
+
385
+ if (stats == null) {
386
+ log.warn("Usage stats not found for resource {}, creating new.", resource.getId());
387
+ Instant windowStart = Instant.now().truncatedTo(ChronoUnit.DAYS);
388
+ Instant windowEnd = windowStart.plus(30, ChronoUnit.DAYS);
389
+ stats = new CloudResourceUsageStats(resource, windowStart, windowEnd);
390
+ resource.setUsageStats(stats);
391
+ // Rely on cascade for initial save
392
+ } else {
393
+ needsSave = true; // Existing stats need explicit save
394
+
395
+ // Check if we need to roll over the stats window
396
+ if (eventTime.isAfter(stats.getTimeWindowEnd())) {
397
+ log.info("Rolling over usage stats window for resource {}", resource.getId());
398
+
399
+ // Create a new window starting from the end of the previous window
400
+ Instant newWindowStart = stats.getTimeWindowEnd();
401
+ Instant newWindowEnd = newWindowStart.plus(30, ChronoUnit.DAYS);
402
+
403
+ // Reset window counters but preserve total cumulative counts
404
+ long totalReadCount = stats.getTotalReadCount();
405
+ long totalWriteCount = stats.getTotalWriteCount();
406
+ long totalEditCount = stats.getTotalEditCount();
407
+
408
+ // Create new stats with the new window
409
+ stats = new CloudResourceUsageStats(resource, newWindowStart, newWindowEnd);
410
+
411
+ // Preserve the cumulative totals
412
+ stats.setTotalReadCount(totalReadCount);
413
+ stats.setTotalWriteCount(totalWriteCount);
414
+ stats.setTotalEditCount(totalEditCount);
415
+
416
+ // Link to resource
417
+ resource.setUsageStats(stats);
418
+ }
419
+ }
420
+
421
+ stats.setLastAccessedAt(eventTime);
422
+
423
+ switch (changeType) {
424
+ case ACCESS:
425
+ stats.incrementReadCount();
426
+ break;
427
+ case UPDATE:
428
+ case PERMISSION: // Treat permission change as an edit
429
+ stats.incrementEditCount();
430
+ break;
431
+ default:
432
+ break; // CREATE/DELETE handled elsewhere
433
+ }
434
+
435
+ if (needsSave) {
436
+ try {
437
+ usageStatsRepository.save(stats);
438
+ log.debug("Updated usage stats for resource: {}", resource.getId());
439
+ } catch (Exception e) {
440
+ log.error("Failed to save usage stats for resource {}: {}", resource.getId(), e.getMessage());
441
+ }
442
+ }
443
+ }
444
+
445
+ private void mapMetadata(CloudResource resource, Map<String, String> details) {
446
+ if (details == null || details.isEmpty())
447
+ return;
448
+ Map<String, String> techMetadata = resource.getTechnicalMetadata();
449
+ Map<String, String> bizMetadata = resource.getBusinessMetadata();
450
+
451
+ details.forEach((key, value) -> {
452
+ if (value == null || value.isBlank())
453
+ return;
454
+ String lowerCaseKey = key.toLowerCase(); // Use lowercase for easier matching
455
+ String providerPrefix = resource.getCloudProviderEnum().name().toLowerCase() + ".";
456
+
457
+ // --- Business Metadata Rules ---
458
+ if (lowerCaseKey.equals(providerPrefix + "actoremail")
459
+ || lowerCaseKey.equals(providerPrefix + "authenticationinfo.principalemail")) {
460
+ bizMetadata.put("owner", value);
461
+ } else if (lowerCaseKey.startsWith(providerPrefix + "resourcelabel.cost-center")) {
462
+ bizMetadata.put("costCenter", value);
463
+ } else if (lowerCaseKey.startsWith(providerPrefix + "resourcelabel.environment")) {
464
+ bizMetadata.put("environment", value);
465
+ } else if (lowerCaseKey.startsWith(providerPrefix + "resourcelabel.application")) {
466
+ bizMetadata.put("applicationName", value);
467
+ } else if (lowerCaseKey.startsWith(providerPrefix + "request.label.owner")
468
+ || lowerCaseKey.startsWith(providerPrefix + "request.label.team")) {
469
+ bizMetadata.put(key.substring((providerPrefix + "request.label.").length()), value);
470
+ }
471
+ // Add more business rules...
472
+
473
+ // --- Technical Metadata Rules ---
474
+ else if (lowerCaseKey.startsWith(providerPrefix + "resourcelabel.")) {
475
+ techMetadata.put(key.substring((providerPrefix + "resourcelabel.").length()), value);
476
+ } else if (lowerCaseKey.startsWith(providerPrefix + "request.label.")) { // Other request labels
477
+ techMetadata.put(key.substring((providerPrefix + "request.label.").length()), value);
478
+ } else if (lowerCaseKey.startsWith(providerPrefix + "loglabel.")) {
479
+ techMetadata.put(key.substring((providerPrefix + "loglabel.").length()), value);
480
+ } else if (lowerCaseKey.startsWith(providerPrefix + "request.")) {
481
+ String requestKey = key.substring((providerPrefix + "request.").length());
482
+ if (requestKey.equals("machineType") || requestKey.equals("zone") ||
483
+ requestKey.equals("region") || requestKey.equals("location") ||
484
+ requestKey.equals("storageClass") || requestKey.equals("datasetId")) {
485
+ techMetadata.put(requestKey, value);
486
+ }
487
+ } else if (lowerCaseKey.startsWith(providerPrefix + "response.")) {
488
+ String responseKey = key.substring((providerPrefix + "response.").length());
489
+ if (responseKey.equals("selfLink") || responseKey.equals("id")) {
490
+ techMetadata.put(responseKey, value);
491
+ }
492
+ } else if (lowerCaseKey.equals(providerPrefix + "resourcetype")
493
+ || lowerCaseKey.equals(providerPrefix + "sourceip")
494
+ || lowerCaseKey.equals(providerPrefix + "useragent")) {
495
+ techMetadata.put(key.substring(providerPrefix.length()), value);
496
+ }
497
+ });
498
+ log.trace("Mapped metadata for resource: {}", resource.getId());
499
+ // Maps are modified directly on the managed entity
500
+ }
501
+ }
src/main/java/com/dalab/discovery/client/cli/ApplicationCommands.java ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.client.cli;
2
+
3
+ import java.util.Arrays;
4
+ import java.util.Optional;
5
+
6
+ import org.springframework.beans.factory.annotation.Autowired;
7
+ import org.springframework.boot.info.BuildProperties;
8
+ import org.springframework.boot.info.GitProperties;
9
+ import org.springframework.core.env.Environment;
10
+ import org.springframework.shell.command.annotation.Command;
11
+ import org.springframework.stereotype.Component;
12
+
13
+ /**
14
+ * Basic application commands available in Spring Shell.
15
+ */
16
+ @Component
17
+ @Command
18
+ public class ApplicationCommands {
19
+
20
+ private final Environment environment;
21
+ private final Optional<BuildProperties> buildProperties;
22
+ private final Optional<GitProperties> gitProperties;
23
+
24
+ @Autowired
25
+ public ApplicationCommands(
26
+ Environment environment,
27
+ Optional<BuildProperties> buildProperties,
28
+ Optional<GitProperties> gitProperties) {
29
+ this.environment = environment;
30
+ this.buildProperties = buildProperties;
31
+ this.gitProperties = gitProperties;
32
+ }
33
+
34
+ /**
35
+ * Display application status and configuration information.
36
+ *
37
+ * @return Application status string
38
+ */
39
+ @Command(command = "app-status", description = "Show application status and configuration")
40
+ public String appStatus() {
41
+ StringBuilder status = new StringBuilder();
42
+ status.append("=== Application Status ===\n");
43
+
44
+ buildProperties.ifPresent(props -> {
45
+ status.append("Name: ").append(props.getName()).append("\n");
46
+ status.append("Version: ").append(props.getVersion()).append("\n");
47
+ status.append("Build Time: ").append(props.getTime()).append("\n");
48
+ });
49
+
50
+ status.append("Active Profiles: ").append(Arrays.toString(environment.getActiveProfiles())).append("\n");
51
+
52
+ gitProperties.ifPresent(props -> {
53
+ status.append("Git Branch: ").append(props.getBranch()).append("\n");
54
+ status.append("Git Commit: ").append(props.getShortCommitId()).append("\n");
55
+ });
56
+
57
+ status.append("Shell Mode: ").append(System.getProperty("app.shell.mode", "false")).append("\n");
58
+
59
+ return status.toString();
60
+ }
61
+
62
+ /**
63
+ * Show active Spring profiles.
64
+ *
65
+ * @return Active profiles string
66
+ */
67
+ @Command(command = "profiles", description = "Show active Spring profiles")
68
+ public String profiles() {
69
+ return "Active Profiles: " + Arrays.toString(environment.getActiveProfiles());
70
+ }
71
+ }
src/main/java/com/dalab/discovery/client/cli/DGCommandLine.java ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.dalab.discovery.client.cli;
2
+
3
+ import org.slf4j.Logger;
4
+ import org.slf4j.LoggerFactory;
5
+
6
+ //@Command(command = "dg")
7
+ public class DGCommandLine {
8
+
9
+ private static final Logger LOGGER = LoggerFactory.getLogger(DGCommandLine.class);
10
+
11
+ // @Autowired
12
+ // GCPCrawler gcpCrawler;
13
+
14
+ // From Here the shell calls the Folders
15
+ // @Command(command = "scanFolder", description = "Start data guardian Folder")
16
+ // public String runFolder(String folderId) {
17
+ // // @Option(
18
+ // // label = "folderName",
19
+ // // description = "Name of the Folder to Scanned ",
20
+ // // defaultValue = "621021804931",
21
+ // // required = true
22
+ // // ) String folderId
23
+
24
+ // LOGGER.debug("Starting data guardian Folder.");
25
+ // long FolderCount = gcpCrawler.fetchFolders(folderId);
26
+ // LOGGER.debug("Crawler has stopped running. Total folder found: {}",
27
+ // FolderCount);
28
+ // return "Spider fetched " + FolderCount + "folders.";
29
+
30
+ // }
31
+
32
+ // @Command(command = "stopFolder", description = "Stop data guardian Folder")
33
+ public String stopFolder() {
34
+ LOGGER.debug("Stopping data guardian Folder.");
35
+
36
+ LOGGER.debug("Folder has stopped running.");
37
+ return "Folder is stopped";
38
+ }
39
+
40
+ // @Command(command = "folderstatus", description = "Get status of data guardian
41
+ // Folder")
42
+ public String getFolderStatus() {
43
+ return "Folder is running";
44
+ }
45
+
46
+ // Fetching assets shell shart from here
47
+ // @Command(command = "runCrawler", description = "Start data guardian ")
48
+ // public String runCrawler() {
49
+ // LOGGER.debug("Starting data guardian .");
50
+ // // long assetCount = gcpCrawler.fetchAssets();
51
+ // LOGGER.debug("Crawler has stopped running. Total assets found: {}",
52
+ // assetCount);
53
+ // return "Spider fetched " + assetCount + "assets.";
54
+ // }
55
+
56
+ // @Command(command = "stopCrawler", description = "Stop data guardian ")
57
+ public String stopCrawler() {
58
+ LOGGER.debug("Stopping data guardian .");
59
+
60
+ LOGGER.debug("Crawler has stopped running.");
61
+ return "Crawler is stopped";
62
+ }
63
+
64
+ // @Command(command = "status", description = "Get status of data guardian ")
65
+ public String getStatus() {
66
+ return "Crawler is running";
67
+ }
68
+ }