Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 9 additions & 29 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.3</version>
<version>3.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
Expand All @@ -19,13 +19,7 @@
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand All @@ -34,7 +28,12 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.3</version>
<version>4.31.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Expand Down Expand Up @@ -64,6 +63,7 @@
<goal>run</goal>
</goals>
<configuration>
<protocArtifact>com.google.protobuf:protoc:4.31.0</protocArtifact>
<inputDirectories>
src/main/java/com/example/demo/resource/proto
</inputDirectories>
Expand All @@ -74,26 +74,6 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-proto-classes</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>src/main/java/com/example/demo/resource/model</outputDirectory>
<resources>
<resource>
<directory>${basedir}/target/generated-sources/com/example/demo/resource/model</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
Expand Down
23 changes: 0 additions & 23 deletions src/main/java/com/example/demo/conf/JaxRsConf.java

This file was deleted.

8 changes: 0 additions & 8 deletions src/main/java/com/example/demo/conf/MediaTypeExt.java

This file was deleted.

35 changes: 0 additions & 35 deletions src/main/java/com/example/demo/conf/ProtoMessageReader.java

This file was deleted.

36 changes: 0 additions & 36 deletions src/main/java/com/example/demo/conf/ProtoMessageWriter.java

This file was deleted.

48 changes: 48 additions & 0 deletions src/main/java/com/example/demo/conf/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.example.demo.conf;

import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// Add ProtobufHttpMessageConverter.
// Spring Boot typically auto-configures this if protobuf-java is present.
// However, explicitly adding it can help ensure it's prioritized or correctly configured,
// especially if there were conflicts or custom media type needs.
// The default ProtobufHttpMessageConverter should handle "application/x-protobuf"
// and "application/protobuf".
converters.add(new ProtobufHttpMessageConverter());
}

// If further customization of ProtobufHttpMessageConverter is needed,
// for example, to use a specific ProtobufJsonFormat parser/printer or to add more media types:
//
// @Bean
// public ProtobufHttpMessageConverter protobufHttpMessageConverter() {
// ProtobufHttpMessageConverter converter = new ProtobufHttpMessageConverter();
// // Example: Customize supported media types if needed
// // converter.setSupportedMediaTypes(List.of(MediaType.valueOf("application/x-protobuf"), MediaType.APPLICATION_JSON));
// // Example: If using protobuf-java-util for JSON format
// // com.google.protobuf.util.JsonFormat.Parser parser = com.google.protobuf.util.JsonFormat.parser().ignoringUnknownFields();
// // com.google.protobuf.util.JsonFormat.Printer printer = com.google.protobuf.util.JsonFormat.printer().preservingProtoFieldNames();
// // return new ProtobufHttpMessageConverter(parser, printer);
// return converter;
// }
//
// And then in configureMessageConverters:
// @Autowired
// private ProtobufHttpMessageConverter protobufHttpMessageConverter;
//
// @Override
// public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// converters.add(protobufHttpMessageConverter);
// }
// For now, the simple addition of a new instance is often enough to ensure it's registered.
}
37 changes: 26 additions & 11 deletions src/main/java/com/example/demo/resource/ClientWebsocketTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,32 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.net.URISyntaxException;

@ClientEndpoint
public class ClientWebsocketTest {
public class ClientWebsocketTest implements AutoCloseable {

private final static Logger logger = LoggerFactory.getLogger(ClientWebsocketTest.class);

private MessageHandler messageHandler;
private String endpointUri;
private Session userSession=null;

public ClientWebsocketTest(String endpoint) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, new URI("ws://localhost:8080/hello"));
} catch (Exception r) {
throw new RuntimeException(r);
}
this.endpointUri = endpoint;
}

public void connect() throws DeploymentException, IOException, URISyntaxException {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, new URI(this.endpointUri));
// Catch block from original constructor is not needed here as per instructions,
// the method signature declares the exceptions to be handled by the caller.
}

@OnOpen
public void myClientOpen(Session session) {
Expand All @@ -34,6 +37,18 @@ public void myClientOpen(Session session) {
}

public void sendMessage(String message) throws IOException {
userSession.getBasicRemote().sendBinary(ByteBuffer.wrap(StandardCharsets.UTF_8.encode(message).array()));
if (userSession != null && userSession.isOpen()) {
userSession.getBasicRemote().sendText(message);
} else {
throw new IOException("WebSocket session is not open.");
}
}

@Override
public void close() throws IOException {
if (userSession != null && userSession.isOpen()) {
logger.info("Closing WebSocket session for ID: {}", userSession.getId());
userSession.close();
}
}
}
78 changes: 78 additions & 0 deletions src/main/java/com/example/demo/resource/TestController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.example.demo.resource;

import com.example.demo.resource.model.PersonBinding;
import com.example.demo.resource.pojo.Citizen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.Collections;

@RestController
@RequestMapping("/api/test")
public class TestController {

private final static Logger logger = LoggerFactory.getLogger(TestController.class);

@GetMapping(produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<String> test() {
try (ClientWebsocketTest clientEndpointTest = new ClientWebsocketTest("ws://localhost:8080/hello")) {
clientEndpointTest.connect(); // New call
clientEndpointTest.sendMessage("Hello, World!");
return ResponseEntity.ok("SUCCESS");
} catch (Exception e) { // Catch DeploymentException, IOException, URISyntaxException
logger.error("Error during WebSocket client test", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("WebSocket test failed: " + e.getMessage());
}
}

@GetMapping(path = "/proto", produces = { MediaType.APPLICATION_JSON_VALUE, "application/x-protobuf" })
public ResponseEntity<?> testProto(@RequestHeader(HttpHeaders.ACCEPT) String acceptHeader) {

if (acceptHeader.isEmpty() || MediaType.APPLICATION_JSON_VALUE.equals(acceptHeader)) {
Citizen john = new Citizen();
john.setId(1234567890);
john.setEmail("john.test@test.com");
john.setName("John Test");
Citizen.Phone phone = new Citizen.Phone();
phone.setType(PersonBinding.Person.PhoneType.MOBILE);
phone.setNumber("761-672-7821");
john.setPhones(Collections.singletonList(phone));
return ResponseEntity.ok(john);
} else {
var name = "John Test";
Integer id = 1234567890;
var email = "john.test@test.com";
var phone = "761-672-7821";
var phoneTyp = PersonBinding.Person.PhoneType.MOBILE;
PersonBinding.Person.Builder personBuilder =
PersonBinding.Person.newBuilder();
personBuilder.setName(name);
personBuilder.setId(id);
personBuilder.setEmail(email);
PersonBinding.Person.PhoneNumber.Builder phoneBuilder
= PersonBinding.Person.PhoneNumber.newBuilder();
phoneBuilder.setNumber(phone);
phoneBuilder.setType(phoneTyp);
personBuilder.addPhones(phoneBuilder.build());
return ResponseEntity.ok(personBuilder.build());
}
}

@PostMapping(path = "/proto", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Citizen> createJson(@RequestBody Citizen citizen) {
logger.info("POST CITIZEN: {}", citizen);
return new ResponseEntity<>(citizen, HttpStatus.CREATED);
}

@PostMapping(path = "/proto", consumes = "application/x-protobuf", produces = "application/x-protobuf")
public ResponseEntity<PersonBinding.Person> createProto(@RequestBody PersonBinding.Person john) {
logger.info("POST Person: {}", john);
return new ResponseEntity<>(john, HttpStatus.CREATED);
}
}
Loading