Skip to content

Commit 0fde584

Browse files
author
Rob Tjalma
authored
Merge pull request #49 from com-pas/async-attempts
Added websockets implementation for Validation
2 parents f6363bb + 28a441a commit 0fde584

23 files changed

+854
-21
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ SPDX-License-Identifier: Apache-2.0
1414

1515
Service to validate SCL Files.
1616

17+
## Rest and Websockets versions
18+
19+
The validation can be done using both Rest and Websockets as transportation technic.
20+
21+
| Method | URL |
22+
|-----------|-----------------------------------------------------------------------|
23+
| Rest | http(s)://**server-address**/compas-scl-validator/validate/v1/{type} |
24+
| Websocket | ws(s)://**server-address**/compas-scl-validator/validate-ws/v1/{type} |
25+
26+
In CoMPAS OpenSCD there is a switch in the setting to indicate if websockets needs to be used. The logic will
27+
automatically determine the URL to be used.
28+
1729
## Development
1830

1931
For the RiseClipse implementation of the validator parts of the RiseClipse project are being used. Currently, these

app/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ SPDX-License-Identifier: Apache-2.0
6565
<groupId>io.quarkus</groupId>
6666
<artifactId>quarkus-resteasy-jaxb</artifactId>
6767
</dependency>
68+
<dependency>
69+
<groupId>io.quarkus</groupId>
70+
<artifactId>quarkus-resteasy-mutiny</artifactId>
71+
</dependency>
72+
<dependency>
73+
<groupId>io.quarkus</groupId>
74+
<artifactId>quarkus-vertx</artifactId>
75+
</dependency>
76+
<dependency>
77+
<groupId>io.quarkus</groupId>
78+
<artifactId>quarkus-websockets</artifactId>
79+
</dependency>
6880
<dependency>
6981
<groupId>io.quarkus</groupId>
7082
<artifactId>quarkus-container-image-docker</artifactId>

app/src/main/java/org/lfenergy/compas/scl/validator/rest/v1/SclValidatorResource.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
package org.lfenergy.compas.scl.validator.rest.v1;
55

66
import io.quarkus.security.Authenticated;
7+
import io.smallrye.mutiny.Uni;
8+
import io.vertx.mutiny.core.eventbus.EventBus;
9+
import io.vertx.mutiny.core.eventbus.Message;
710
import org.lfenergy.compas.scl.extensions.model.SclFileType;
11+
import org.lfenergy.compas.scl.validator.rest.v1.event.SclValidatorEventRequest;
812
import org.lfenergy.compas.scl.validator.rest.v1.model.SclValidateRequest;
913
import org.lfenergy.compas.scl.validator.rest.v1.model.SclValidateResponse;
10-
import org.lfenergy.compas.scl.validator.service.SclValidatorService;
1114

1215
import javax.enterprise.context.RequestScoped;
1316
import javax.inject.Inject;
@@ -21,20 +24,21 @@
2124
@RequestScoped
2225
@Path("/validate/v1/{" + TYPE_PATH_PARAM + "}")
2326
public class SclValidatorResource {
24-
private final SclValidatorService sclValidatorService;
27+
private final EventBus eventBus;
2528

2629
@Inject
27-
public SclValidatorResource(SclValidatorService compasCimMappingService) {
28-
this.sclValidatorService = compasCimMappingService;
30+
public SclValidatorResource(EventBus eventBus) {
31+
this.eventBus = eventBus;
2932
}
3033

3134
@POST
3235
@Consumes(MediaType.APPLICATION_XML)
3336
@Produces(MediaType.APPLICATION_XML)
34-
public SclValidateResponse validateSCL(@PathParam(TYPE_PATH_PARAM) SclFileType type,
35-
@Valid SclValidateRequest request) {
36-
var response = new SclValidateResponse();
37-
response.setValidationErrorList(sclValidatorService.validate(type, request.getSclData()));
38-
return response;
37+
public Uni<SclValidateResponse> validateSCL(@PathParam(TYPE_PATH_PARAM) SclFileType type,
38+
@Valid SclValidateRequest request) {
39+
return eventBus.<SclValidateResponse>request(
40+
"validate-rest",
41+
new SclValidatorEventRequest(type, request.getSclData()))
42+
.onItem().transform(Message::body);
3943
}
40-
}
44+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-FileCopyrightText: 2022 Alliander N.V.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
package org.lfenergy.compas.scl.validator.rest.v1;
5+
6+
import io.quarkus.security.Authenticated;
7+
import io.vertx.mutiny.core.eventbus.EventBus;
8+
import org.lfenergy.compas.scl.extensions.model.SclFileType;
9+
import org.lfenergy.compas.scl.validator.rest.v1.event.SclValidatorEventRequest;
10+
import org.lfenergy.compas.scl.validator.rest.v1.model.SclValidateRequest;
11+
import org.lfenergy.compas.scl.validator.rest.v1.websocket.SclValidateRequestDecoder;
12+
import org.lfenergy.compas.scl.validator.rest.v1.websocket.SclValidateRequestEncoder;
13+
import org.lfenergy.compas.scl.validator.rest.v1.websocket.SclValidateResponseDecoder;
14+
import org.lfenergy.compas.scl.validator.rest.v1.websocket.SclValidateResponseEncoder;
15+
import org.slf4j.Logger;
16+
import org.slf4j.LoggerFactory;
17+
18+
import javax.enterprise.context.ApplicationScoped;
19+
import javax.inject.Inject;
20+
import javax.websocket.*;
21+
import javax.websocket.server.PathParam;
22+
import javax.websocket.server.ServerEndpoint;
23+
24+
import static org.lfenergy.compas.scl.validator.rest.SclResourceConstants.TYPE_PATH_PARAM;
25+
26+
@Authenticated
27+
@ApplicationScoped
28+
@ServerEndpoint(value = "/compas-scl-validator/validate-ws/v1/{" + TYPE_PATH_PARAM + "}",
29+
decoders = {SclValidateRequestDecoder.class, SclValidateResponseDecoder.class},
30+
encoders = {SclValidateRequestEncoder.class, SclValidateResponseEncoder.class})
31+
public class SclValidatorServerEndpoint {
32+
private static final Logger LOGGER = LoggerFactory.getLogger(SclValidatorServerEndpoint.class);
33+
34+
private final EventBus eventBus;
35+
36+
@Inject
37+
public SclValidatorServerEndpoint(EventBus eventBus) {
38+
this.eventBus = eventBus;
39+
}
40+
41+
@OnOpen
42+
public void onOpen(Session session, @PathParam(TYPE_PATH_PARAM) String type) {
43+
LOGGER.debug("Starting session {} for type {}.", session.getId(), type);
44+
}
45+
46+
@OnMessage
47+
public void onMessage(Session session, SclValidateRequest request, @PathParam(TYPE_PATH_PARAM) String type) {
48+
LOGGER.info("Message from session {} for type {}.", session.getId(), type);
49+
eventBus.send("validate-ws", new SclValidatorEventRequest(
50+
session, SclFileType.valueOf(type), request.getSclData()));
51+
}
52+
53+
@OnError
54+
public void onError(Session session, @PathParam(TYPE_PATH_PARAM) String type, Throwable throwable) {
55+
LOGGER.warn("Error with session {} for type {}.", session.getId(), type, throwable);
56+
}
57+
58+
@OnClose
59+
public void onClose(Session session, @PathParam(TYPE_PATH_PARAM) String type) {
60+
LOGGER.debug("Closing session {} for type {}.", session.getId(), type);
61+
}
62+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-FileCopyrightText: 2022 Alliander N.V.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
package org.lfenergy.compas.scl.validator.rest.v1.event;
5+
6+
import io.quarkus.vertx.ConsumeEvent;
7+
import io.smallrye.mutiny.Uni;
8+
import org.lfenergy.compas.scl.validator.rest.v1.model.SclValidateResponse;
9+
import org.lfenergy.compas.scl.validator.service.SclValidatorService;
10+
11+
import javax.enterprise.context.ApplicationScoped;
12+
import javax.inject.Inject;
13+
14+
/**
15+
* Event Handler used to execute the validation asynchronized.
16+
*/
17+
@ApplicationScoped
18+
public class SclValidatorEventHandler {
19+
private final SclValidatorService sclValidatorService;
20+
21+
@Inject
22+
public SclValidatorEventHandler(SclValidatorService sclValidatorService) {
23+
this.sclValidatorService = sclValidatorService;
24+
}
25+
26+
@ConsumeEvent(value = "validate-ws", blocking = true)
27+
public void validateWebsocketsEvent(SclValidatorEventRequest request) {
28+
var response = new SclValidateResponse();
29+
response.setValidationErrorList(sclValidatorService.validate(request.getType(), request.getSclData()));
30+
31+
var session = request.getSession();
32+
session.getAsyncRemote().sendObject(response);
33+
}
34+
35+
@ConsumeEvent(value = "validate-rest", blocking = true)
36+
public Uni<SclValidateResponse> validateRestEvent(SclValidatorEventRequest request) {
37+
var response = new SclValidateResponse();
38+
response.setValidationErrorList(sclValidatorService.validate(request.getType(), request.getSclData()));
39+
return Uni.createFrom().item(response);
40+
}
41+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-FileCopyrightText: 2022 Alliander N.V.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
package org.lfenergy.compas.scl.validator.rest.v1.event;
5+
6+
import org.lfenergy.compas.scl.extensions.model.SclFileType;
7+
8+
import javax.websocket.Session;
9+
10+
public class SclValidatorEventRequest {
11+
private Session session;
12+
private final SclFileType type;
13+
private final String sclData;
14+
15+
public SclValidatorEventRequest(SclFileType type, String sclData) {
16+
this(null, type, sclData);
17+
}
18+
19+
public SclValidatorEventRequest(Session session, SclFileType type, String sclData) {
20+
this.session = session;
21+
this.type = type;
22+
this.sclData = sclData;
23+
}
24+
25+
public Session getSession() {
26+
return session;
27+
}
28+
29+
public SclFileType getType() {
30+
return type;
31+
}
32+
33+
public String getSclData() {
34+
return sclData;
35+
}
36+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-FileCopyrightText: 2022 Alliander N.V.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
package org.lfenergy.compas.scl.validator.rest.v1.websocket;
5+
6+
import org.lfenergy.compas.scl.validator.exception.SclValidatorException;
7+
import org.lfenergy.compas.scl.validator.rest.v1.model.SclValidateRequest;
8+
9+
import javax.websocket.Decoder;
10+
import javax.websocket.EndpointConfig;
11+
import javax.xml.bind.JAXBContext;
12+
import java.io.StringReader;
13+
14+
import static org.lfenergy.compas.scl.validator.exception.SclValidatorErrorCode.WEBSOCKET_DECODER_ERROR_CODE;
15+
16+
public class SclValidateRequestDecoder implements Decoder.Text<SclValidateRequest> {
17+
@Override
18+
public void init(EndpointConfig endpointConfig) {
19+
// do nothing.
20+
}
21+
22+
@Override
23+
public boolean willDecode(String message) {
24+
return (message != null);
25+
}
26+
27+
@Override
28+
public SclValidateRequest decode(String message) {
29+
try {
30+
var jaxbContext = JAXBContext.newInstance(SclValidateRequest.class);
31+
var unmarshaller = jaxbContext.createUnmarshaller();
32+
var reader = new StringReader(message);
33+
return (SclValidateRequest) unmarshaller.unmarshal(reader);
34+
} catch (Exception exp) {
35+
throw new SclValidatorException(WEBSOCKET_DECODER_ERROR_CODE,
36+
"Error unmarshalling SCL Validate Request from Websockets.",
37+
exp);
38+
}
39+
}
40+
41+
@Override
42+
public void destroy() {
43+
// do nothing.
44+
}
45+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-FileCopyrightText: 2022 Alliander N.V.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
package org.lfenergy.compas.scl.validator.rest.v1.websocket;
5+
6+
import org.lfenergy.compas.scl.validator.exception.SclValidatorException;
7+
import org.lfenergy.compas.scl.validator.rest.v1.model.SclValidateRequest;
8+
9+
import javax.websocket.Encoder;
10+
import javax.websocket.EndpointConfig;
11+
import javax.xml.bind.JAXBContext;
12+
import java.io.StringWriter;
13+
14+
import static org.lfenergy.compas.scl.validator.exception.SclValidatorErrorCode.WEBSOCKET_ENCODER_ERROR_CODE;
15+
16+
public class SclValidateRequestEncoder implements Encoder.Text<SclValidateRequest> {
17+
@Override
18+
public void init(EndpointConfig endpointConfig) {
19+
// do nothing.
20+
}
21+
22+
@Override
23+
public String encode(SclValidateRequest request) {
24+
try {
25+
var jaxbContext = JAXBContext.newInstance(SclValidateRequest.class);
26+
var marshaller = jaxbContext.createMarshaller();
27+
28+
var st = new StringWriter();
29+
marshaller.marshal(request, st);
30+
return st.toString();
31+
} catch (Exception exp) {
32+
throw new SclValidatorException(WEBSOCKET_ENCODER_ERROR_CODE,
33+
"Error marshalling SCL Validate Request from Websockets.",
34+
exp);
35+
}
36+
}
37+
38+
@Override
39+
public void destroy() {
40+
// do nothing.
41+
}
42+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-FileCopyrightText: 2022 Alliander N.V.
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
package org.lfenergy.compas.scl.validator.rest.v1.websocket;
5+
6+
import org.lfenergy.compas.scl.validator.exception.SclValidatorException;
7+
import org.lfenergy.compas.scl.validator.rest.v1.model.SclValidateResponse;
8+
9+
import javax.websocket.Decoder;
10+
import javax.websocket.EndpointConfig;
11+
import javax.xml.bind.JAXBContext;
12+
import java.io.StringReader;
13+
14+
import static org.lfenergy.compas.scl.validator.exception.SclValidatorErrorCode.WEBSOCKET_DECODER_ERROR_CODE;
15+
16+
public class SclValidateResponseDecoder implements Decoder.Text<SclValidateResponse> {
17+
@Override
18+
public void init(EndpointConfig endpointConfig) {
19+
// do nothing.
20+
}
21+
22+
@Override
23+
public boolean willDecode(String message) {
24+
return (message != null);
25+
}
26+
27+
@Override
28+
public SclValidateResponse decode(String message) {
29+
try {
30+
var jaxbContext = JAXBContext.newInstance(SclValidateResponse.class);
31+
var unmarshaller = jaxbContext.createUnmarshaller();
32+
var reader = new StringReader(message);
33+
return (SclValidateResponse) unmarshaller.unmarshal(reader);
34+
} catch (Exception exp) {
35+
throw new SclValidatorException(WEBSOCKET_DECODER_ERROR_CODE,
36+
"Error unmarshalling SCL Validate Response from Websockets.",
37+
exp);
38+
}
39+
}
40+
41+
@Override
42+
public void destroy() {
43+
// do nothing.
44+
}
45+
}

0 commit comments

Comments
 (0)