Skip to content

Commit 92e21c7

Browse files
author
Robert Winkler
committed
Added LMOS Protocol and moved messages
1 parent cf5e9e2 commit 92e21c7

File tree

49 files changed

+621
-192
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+621
-192
lines changed

kotlin-wot-binding-http/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies {
1515
implementation("io.ktor:ktor-server-content-negotiation")
1616
implementation("io.ktor:ktor-client-content-negotiation")
1717
implementation("io.ktor:ktor-serialization-jackson")
18+
implementation("io.ktor:ktor-server-metrics-micrometer")
1819
implementation("io.ktor:ktor-server-auto-head-response")
1920
testImplementation("io.ktor:ktor-server-test-host")
2021
testImplementation("ch.qos.logback:logback-classic:1.5.12")

kotlin-wot-binding-http/src/main/kotlin/http/HttpProtocolClient.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,13 @@ class HttpProtocolClient(
100100
private suspend fun resolveRequestToContent(form: Form, method: HttpMethod, content: Content? = null): Content {
101101
return try {
102102
val response: HttpResponse = client.request(form.href) {
103+
headers {
104+
append(HttpHeaders.Accept, form.contentType)
105+
}
103106
this.method = method
104107
content?.let {
105108
headers {
106-
append(HttpHeaders.ContentType, it.type ?: ContentType.Application.Json.toString())
109+
append(HttpHeaders.ContentType, it.type )
107110
}
108111
setBody(it.body)
109112
}

kotlin-wot-binding-http/src/main/kotlin/http/HttpProtocolServer.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import io.ktor.http.*
2222
import io.ktor.serialization.jackson.*
2323
import io.ktor.server.application.*
2424
import io.ktor.server.engine.*
25+
import io.ktor.server.metrics.micrometer.*
2526
import io.ktor.server.netty.*
2627
import io.ktor.server.plugins.contentnegotiation.*
2728
import io.ktor.server.plugins.statuspages.*
@@ -30,6 +31,9 @@ import io.ktor.server.response.*
3031
import io.ktor.server.routing.*
3132
import io.ktor.util.reflect.*
3233
import io.ktor.utils.io.*
34+
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
35+
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
36+
import io.micrometer.core.instrument.binder.system.ProcessorMetrics
3337
import org.slf4j.LoggerFactory
3438
import kotlin.collections.set
3539

@@ -204,6 +208,13 @@ fun Application.setupRouting(servient: Servient) {
204208
throw cause // re-throw if you want it to be logged
205209
}
206210
}
211+
install(MicrometerMetrics) {
212+
meterBinders = listOf(
213+
JvmMemoryMetrics(),
214+
JvmGcMetrics(),
215+
ProcessorMetrics()
216+
)
217+
}
207218
setupJackson()
208219
routing {
209220
route("/") {

kotlin-wot-binding-websocket/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44

55
dependencies {
66
api(project(":kotlin-wot"))
7+
api(project(":kotlin-wot-lmos-protocol"))
78
implementation("org.slf4j:slf4j-api")
89
implementation("io.ktor:ktor-server-netty")
910
implementation("io.ktor:ktor-server-websockets")
@@ -14,6 +15,7 @@ dependencies {
1415
implementation("io.ktor:ktor-client-logging")
1516
implementation("io.ktor:ktor-server-call-logging")
1617
implementation("io.ktor:ktor-serialization-jackson")
18+
implementation("io.ktor:ktor-server-metrics-micrometer")
1719
testImplementation("io.ktor:ktor-server-test-host")
1820
testImplementation(project(":kotlin-wot-binding-http"))
1921
testImplementation("ch.qos.logback:logback-classic:1.5.12")

kotlin-wot-binding-websocket/src/main/kotlin/websocket/WebSocketProtocolServer.kt

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package ai.ancf.lmos.wot.binding.websocket
33
import ai.ancf.lmos.wot.JsonMapper
44
import ai.ancf.lmos.wot.Servient
55
import ai.ancf.lmos.wot.content.ContentManager
6+
import ai.ancf.lmos.wot.protocol.LMOSContext
7+
import ai.ancf.lmos.wot.protocol.LMOS_PROTOCOL_NAME
68
import ai.ancf.lmos.wot.thing.ExposedThing
79
import ai.ancf.lmos.wot.thing.ThingDescription
810
import ai.ancf.lmos.wot.thing.form.Form
@@ -18,16 +20,22 @@ import io.ktor.http.*
1820
import io.ktor.serialization.jackson.*
1921
import io.ktor.server.application.*
2022
import io.ktor.server.engine.*
23+
import io.ktor.server.metrics.micrometer.*
2124
import io.ktor.server.netty.*
2225
import io.ktor.server.plugins.calllogging.*
2326
import io.ktor.server.plugins.contentnegotiation.*
2427
import io.ktor.server.routing.*
2528
import io.ktor.server.websocket.*
2629
import io.ktor.util.reflect.*
2730
import io.ktor.websocket.*
31+
import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics
32+
import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
33+
import io.micrometer.core.instrument.binder.system.ProcessorMetrics
2834
import org.slf4j.LoggerFactory
2935
import java.util.*
3036

37+
private const val SUB_PROTOCOL_MEDIA_TYPE = "application/json"
38+
3139
class WebSocketProtocolServer(
3240
private val wait: Boolean = false,
3341
private val bindHost: String = "0.0.0.0",
@@ -95,9 +103,9 @@ class WebSocketProtocolServer(
95103
// Create a single form that includes all operations and the subprotocol
96104
val form = Form(
97105
href = href,
98-
contentType = "application/json",
106+
contentType = SUB_PROTOCOL_MEDIA_TYPE,
99107
op = operations,
100-
subprotocol = "webthingprotocol"
108+
subprotocol = LMOS_PROTOCOL_NAME
101109
)
102110

103111
property.forms += form
@@ -113,14 +121,14 @@ class WebSocketProtocolServer(
113121
// Create a form for invoking the action
114122
val form = Form(
115123
href = href,
116-
contentType = "application/json",
124+
contentType = SUB_PROTOCOL_MEDIA_TYPE,
117125
op = listOf(Operation.INVOKE_ACTION), // Operation type specific to actions
118-
subprotocol = "webthingprotocol" // Specific subprotocol for actions
126+
subprotocol = LMOS_PROTOCOL_NAME // Specific subprotocol for actions
119127
)
120128

121129
// Add the form to the action's forms
122130
action.forms += form
123-
log.debug("Assign '{}' with subprotocol '{}' to Action '{}'", href, "webthingprotocol", name)
131+
log.debug("Assign '{}' with subprotocol '{}' to Action '{}'", href, LMOS_PROTOCOL_NAME, name)
124132
}
125133
}
126134

@@ -132,14 +140,14 @@ class WebSocketProtocolServer(
132140
// Create a form for subscribing to the event
133141
val form = Form(
134142
href = href,
135-
contentType = "application/json",
136-
subprotocol = "webthingprotocol",
143+
contentType = SUB_PROTOCOL_MEDIA_TYPE,
144+
subprotocol = LMOS_PROTOCOL_NAME,
137145
op = listOf(Operation.SUBSCRIBE_EVENT, Operation.UNSUBSCRIBE_EVENT) // Operation type specific to events
138146
)
139147

140148
// Add the form to the event's forms
141149
event.forms += form
142-
log.debug("Assign '{}' with subprotocol '{}' to Event '{}'", href, "webthingprotocol", name)
150+
log.debug("Assign '{}' with subprotocol '{}' to Event '{}'", href, LMOS_PROTOCOL_NAME, name)
143151
}
144152
}
145153
}
@@ -159,6 +167,13 @@ fun Application.setupRoutingWithWebSockets(servient: Servient) {
159167
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
160168
}
161169
}
170+
install(MicrometerMetrics) {
171+
meterBinders = listOf(
172+
JvmMemoryMetrics(),
173+
JvmGcMetrics(),
174+
ProcessorMetrics()
175+
)
176+
}
162177
install(WebSockets) {
163178
contentConverter = JacksonWebsocketContentConverter(JsonMapper.instance)
164179
}
@@ -438,94 +453,94 @@ private fun createUnsupportedMessageType(thingId: String, correlationId : String
438453
return ErrorMessage(
439454
thingId = thingId,
440455
correlationId = correlationId,
441-
type = "https://w3c.github.io/web-thing-protocol/errors#unsupported-message-type",
456+
type = "${LMOSContext.url}/errors#unsupported-message-type",
442457
title = "Unsupported Message Type",
443458
status = "400",
444459
detail = "Unsupported message type",
445-
instance = "https://mythingserver.com/errors/${UUID.randomUUID()}"
460+
instance = "${LMOSContext.url}/errors/${UUID.randomUUID()}"
446461
)
447462
}
448463

449464
private fun createPropertyIsReadOnly(thingId: String, correlationId : String, propertyName: String?): ErrorMessage {
450465
return ErrorMessage(
451466
thingId = thingId,
452467
correlationId = correlationId,
453-
type = "https://w3c.github.io/web-thing-protocol/errors#operation-not-allowed",
468+
type = "${LMOSContext.url}/errors#operation-not-allowed",
454469
title = "Property Read-Only",
455470
status = "405",
456471
detail = "Property '$propertyName' is read-only.",
457-
instance = "https://mythingserver.com/errors/${UUID.randomUUID()}"
472+
instance = "${LMOSContext.url}/errors/${UUID.randomUUID()}"
458473
)
459474
}
460475

461476
private fun createPropertyIsWriteOnly(thingId: String, correlationId : String, propertyName: String?): ErrorMessage {
462477
return ErrorMessage(
463478
thingId = thingId,
464479
correlationId = correlationId,
465-
type = "https://w3c.github.io/web-thing-protocol/errors#operation-not-allowed",
480+
type = "${LMOSContext.url}/errors#operation-not-allowed",
466481
title = "Property is write-only",
467482
status = "405",
468483
detail = "Property '$propertyName' is write-only.",
469-
instance = "https://mythingserver.com/errors/${UUID.randomUUID()}"
484+
instance = "${LMOSContext.url}/errors/${UUID.randomUUID()}"
470485
)
471486
}
472487

473488
private fun createThingNotFound(thingId: String, correlationId : String): ErrorMessage {
474489
return ErrorMessage(
475490
thingId = thingId,
476491
correlationId = correlationId,
477-
type = "https://w3c.github.io/web-thing-protocol/errors#not-found",
492+
type = "${LMOSContext.url}/errors#not-found",
478493
title = "Thing Not Found",
479494
status = "404",
480495
detail = "Thing with ID '$thingId' not found.",
481-
instance = "https://mythingserver.com/errors/${UUID.randomUUID()}"
496+
instance = "${LMOSContext.url}/errors/${UUID.randomUUID()}"
482497
)
483498
}
484499

485500
private fun createInternalServerError(thingId: String, correlationId : String, exceptionMessage: String?): ErrorMessage {
486501
return ErrorMessage(
487502
thingId = thingId,
488503
correlationId = correlationId,
489-
type = "https://w3c.github.io/web-thing-protocol/errors#internal-server-error",
504+
type = "${LMOSContext.url}/errors#internal-server-error",
490505
title = "Internal Server Error",
491506
status = "500",
492-
detail = "Error reading property: $exceptionMessage",
493-
instance = "https://mythingserver.com/errors/${UUID.randomUUID()}"
507+
detail = "Internal Server Error: $exceptionMessage",
508+
instance = "${LMOSContext.url}/errors/${UUID.randomUUID()}"
494509
)
495510
}
496511

497512
private fun createPropertyNotFound(thingId: String, correlationId : String, propertyName: String?): ErrorMessage {
498513
return ErrorMessage(
499514
thingId = thingId,
500515
correlationId = correlationId,
501-
type = "https://w3c.github.io/web-thing-protocol/errors#not-found",
516+
type = "${LMOSContext.url}/errors#not-found",
502517
title = "Property Not Found",
503518
status = "404",
504519
detail = "Property '$propertyName' not found.",
505-
instance = "https://mythingserver.com/errors/${UUID.randomUUID()}"
520+
instance = "${LMOSContext.url}/errors/${UUID.randomUUID()}"
506521
)
507522
}
508523

509524
private fun createEventNotFound(thingId: String, correlationId : String, eventName: String?): ErrorMessage {
510525
return ErrorMessage(
511526
thingId = thingId,
512527
correlationId = correlationId,
513-
type = "https://w3c.github.io/web-thing-protocol/errors#not-found",
528+
type = "${LMOSContext.url}/errors#not-found",
514529
title = "Event Not Found",
515530
status = "404",
516531
detail = "Event '$eventName' not found.",
517-
instance = "https://mythingserver.com/errors/${UUID.randomUUID()}"
532+
instance = "${LMOSContext.url}/errors/${UUID.randomUUID()}"
518533
)
519534
}
520535

521536
private fun createActionNotFound(thingId: String, correlationId : String, actionName: String?): ErrorMessage {
522537
return ErrorMessage(
523538
thingId = thingId,
524539
correlationId = correlationId,
525-
type = "https://w3c.github.io/web-thing-protocol/errors#not-found",
540+
type = "${LMOSContext.url}/errors#not-found",
526541
title = "Action Not Found",
527542
status = "404",
528543
detail = "Action '$actionName' not found.",
529-
instance = "https://mythingserver.com/errors/${UUID.randomUUID()}"
544+
instance = "${LMOSContext.url}/errors/${UUID.randomUUID()}"
530545
)
531546
}

kotlin-wot-integration-tests/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies {
99
api(project(":kotlin-wot-binding-websocket"))
1010
api(project(":kotlin-wot-binding-mqtt"))
1111
api(project(":kotlin-wot-spring-boot-starter"))
12+
api(project(":kotlin-wot-lmos-protocol"))
1213
implementation("ai.ancf.lmos:arc-azure-client:0.111.0")
1314
api("ai.ancf.lmos:arc-spring-boot-starter:0.111.0")
1415

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package ai.ancf.lmos.wot.integration
2+
3+
import org.springframework.boot.autoconfigure.SpringBootApplication
4+
import org.springframework.boot.runApplication
5+
6+
7+
fun main(args: Array<String>) {
8+
runApplication<AgentApplication>(*args)
9+
}
10+
11+
@SpringBootApplication
12+
class AgentApplication {
13+
14+
}

0 commit comments

Comments
 (0)