Skip to content
Merged

Spring4 #3907

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
7954caa
Phased upgrade of springboot
shroffk Aug 4, 2026
5723ffc
update save and restore - spring 3, jakarta
shroffk Aug 4, 2026
6188eab
spring 4.1
shroffk Aug 4, 2026
ad7a641
now try to make save and restore to spring4.1
shroffk Aug 4, 2026
b03359b
start migration to new spring security API
shroffk Aug 4, 2026
e0d2e1f
updating save and restore tests for springboot 4.1
shroffk Aug 4, 2026
d98fd57
use named path variables as required by springboot4
shroffk Aug 5, 2026
18eefbe
fix windows test for the Path calculations
shroffk Aug 5, 2026
bc14788
Add the need compiler flag "-parameter"
shroffk Aug 5, 2026
52a0484
switch some of the tests to use real SnapshotUtil
shroffk Aug 5, 2026
7ceb150
moving to jdk25 and javafx25
shroffk Aug 6, 2026
16ad5da
upgrade springdoc version
shroffk Aug 6, 2026
f75352f
updating the elastic client to 9.4.4
shroffk Aug 6, 2026
413818b
consistent switch to low level elastic rest client
shroffk Aug 6, 2026
cd3a07e
first pass of replacing elastic high level client with low level
shroffk Aug 6, 2026
86d8ee9
move sar to the low level elastic rest client
shroffk Aug 7, 2026
c82c486
switching all remaining elastic high level client usage to low level …
shroffk Aug 10, 2026
4ea5933
Merge pull request #3906 from ControlSystemStudio/jdk-25
shroffk Aug 11, 2026
26a3314
cleanup the local test docker file
shroffk Aug 18, 2026
fcd1d3b
Moving all phoebus modules to springboot 4.1.0
shroffk Aug 18, 2026
6bb1ad0
update SAR properties for elastic 9.4.4
shroffk Aug 18, 2026
cbaad62
Changes due to changed web socket connection API, which also allows f…
georgweiss Aug 20, 2026
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
2 changes: 1 addition & 1 deletion .github/CI_VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ file is the checklist of what needs periodic review and where each pin lives.

| What | Version | Defined in | Notes |
|------|---------|------------|-------|
| Java (JDK) | `21` | `.github/actions/setup-java/action.yml` | Single source of truth for the CI JDK. Must match `maven.compiler.source`/`maven.compiler.target` in `pom.xml`. Review when the project adopts a new LTS. |
| Java (JDK) | `25` | `.github/actions/setup-java/action.yml` | Single source of truth for the CI JDK. Must match `maven.compiler.source`/`maven.compiler.target` in `pom.xml` and the `openjfx.version` line. Review when the project adopts a new LTS. |
| Elasticsearch | `8.11.2` | `.github/workflows/_integration-test.yml` and `services/save-and-restore/docker-compose.yml` | Service container for the save-and-restore integration tests. Update both files together. |

## Automatically maintained
Expand Down
2 changes: 1 addition & 1 deletion .github/actions/setup-java/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ runs:
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287
with:
distribution: temurin
java-version: '21'
java-version: '25'
cache: maven
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public void testDirectory()
}

@Test
@DisabledOnOs(OS.WINDOWS)
public void testCombineNotWindows()
{
String path = ModelResourceUtil.combineDisplayPaths(null, "example.opi");
Expand Down
11 changes: 10 additions & 1 deletion app/save-and-restore/model/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,21 @@
<version>${tools.jackson.version}</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.glassfish/javax.json -->
<!-- Old Glassfish javax.json impl — required by org.epics:vtype-json which
uses javax.json.spi.JsonProvider SPI. Must coexist with jakarta.json
because the two SPI registrations are under different class names. -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.4</version>
</dependency>
<!-- New Glassfish jakarta.json impl — satisfies jakarta.json.spi.JsonProvider
SPI used by elasticsearch-java 9.x and other Jakarta EE consumers. -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.json</artifactId>
<version>2.0.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;

import javax.websocket.DeploymentException;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -37,7 +35,7 @@
* <li>Manages keep alive as supported by the Spring Framework libs</li>
* <li>Passes string messages to registered {@link WebSocketMessageHandler}a</li>
* <li>Calls {@link Runnable}s (if specified) to signal connection or disconnection</li>
* <li>Attempts to auto-reconnect if web socket is closed by remote peer.</li>
* <li>Attempts to auto-reconnect if web socket is closed by remote peer or if connection attempt fails.</li>
* </ul>
* <p>
* All messages received from the remote peer are strings only, but may be JSON formatted.
Expand All @@ -56,12 +54,13 @@
*/
public class WebSocketClientService {


private StompSession stompSession;
private Runnable connectCallback;
private Runnable disconnectCallback;
private final List<WebSocketMessageHandler> webSocketMessageHandlers = Collections.synchronizedList(new ArrayList<>());
private final AtomicBoolean attemptReconnect = new AtomicBoolean();
private final WebSocketClient webSocketClient = new StandardWebSocketClient();
private final WebSocketStompClient stompClient;
private final StompSessionHandler sessionHandler = new StompSessionHandler();
/**
* Full path to the web socket connection URL, e.g. ws://localhost:8080/Olog/web-socket
*/
Expand All @@ -88,6 +87,12 @@
path = path.substring(0, path.length() - 1);
}
this.subscriptionEndpoint = path + Constants.MESSAGES;
stompClient = new WebSocketStompClient(webSocketClient);
stompClient.setMessageConverter(new StringMessageConverter());
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.initialize();
stompClient.setTaskScheduler(threadPoolTaskScheduler);
stompClient.setDefaultHeartbeat(new long[]{60000, 60000});
}

/**
Expand Down Expand Up @@ -122,62 +127,20 @@
}

/**
* Disconnects the socket if connected and terminates connection thread.
* Disconnects the socket if connected.
*/
public synchronized void shutdown() {
attemptReconnect.set(false);
public void shutdown() {
if (stompSession != null && stompSession.isConnected()) {
stompSession.disconnect();
}
}

/**
* Attempts to connect to the remote peer, both in initial connection and in a reconnection scenario.
* If connection fails, new attempts are made every 10s until successful.
* Attempts to connect to the remote peer.
*/
public void connect() {
attemptReconnect.set(true);
WebSocketClient webSocketClient = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
stompClient.setMessageConverter(new StringMessageConverter());
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.initialize();
stompClient.setTaskScheduler(threadPoolTaskScheduler);
stompClient.setDefaultHeartbeat(new long[]{60000, 60000});
StompSessionHandler sessionHandler = new StompSessionHandler();
logger.log(Level.INFO, "Attempting web socket connection to " + connectUrl);
new Thread(() -> {
while (true) {
try {
synchronized (WebSocketClientService.this) {
if (attemptReconnect.get()) {
stompSession = stompClient.connect(connectUrl, sessionHandler).get();
stompSession.subscribe(this.subscriptionEndpoint, new StompFrameHandler() {
@Override
public Type getPayloadType(StompHeaders headers) {
return String.class;
}

@Override
public void handleFrame(StompHeaders headers, Object payload) {
logger.log(Level.INFO, "Handling subscription frame: " + payload);
webSocketMessageHandlers.forEach(h -> h.handleWebSocketMessage((String) payload));
}
});
attemptReconnect.set(false);
}
break;
}
} catch (Exception e) {
logger.log(Level.WARNING, "Got exception when trying to connect", e);
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
logger.log(Level.WARNING, "Got exception when putting thread to sleep", e);
}
}
}).start();
stompClient.connectAsync(connectUrl, sessionHandler);
}

/**
Expand All @@ -199,22 +162,34 @@
}

/**
* Handles connection success callback: thread to attempt connection is aborted,
* and connect callback is called, if set by API client.
* Registers subscription for messages.
*
* @param session the client STOMP session
* @param connectedHeaders the STOMP CONNECTED frame headers
*/
@Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
stompSession = session;
logger.log(Level.INFO, "Connected to web socket");
stompSession.subscribe(subscriptionEndpoint, new StompFrameHandler() {
@Override
public Type getPayloadType(StompHeaders headers) {
return String.class;
}

@Override
public void handleFrame(StompHeaders headers, Object payload) {
logger.log(Level.INFO, "Handling subscription frame: " + payload);
webSocketMessageHandlers.forEach(h -> h.handleWebSocketMessage((String) payload));
}
});
if (connectCallback != null) {
connectCallback.run();
}
}

/**
* Hit for instance if an attempt is made to send a message to peer after {@link StompSession} has been closed.
* Called when - for instance - an attempt is made to send a message to peer after {@link StompSession} has been closed.
*
* @param session the client STOMP session
* @param command the STOMP command of the frame
Expand All @@ -229,30 +204,22 @@
}

/**
* Handles error for different type of {@link Exception}s:
* <ol>
* <li>{@link DeploymentException}: unable to connect, i.e. do not start a new connection thread.</li>
* <li>{@link ConnectionLostException}: service not reachable, e.g. due to network issues or service down.
* Connection thread started.</li>
* <li>{@link IllegalStateException}: service very busy or being debugged, i.e. heartbeat messages
* not received. Connection thread started.</li>
* </ol>
* Called if connection fails or if connection is lost (e.g. remote service is terminated).
*
* @param session the client STOMP session
* @param exception the exception that occurred. This is evaluated to determine if a reconnection
* thread should be launched.
* @param exception the exception that occurred.
*/
@Override
public void handleTransportError(StompSession session, Throwable exception) {
if(exception instanceof DeploymentException){
logger.log(Level.WARNING, "Unable to connect", exception);
logger.log(Level.WARNING, "Connection failed or lost, will attempt to reconnect", exception);
if (disconnectCallback != null && exception instanceof ConnectionLostException) {
disconnectCallback.run();
}
else {
logger.log(Level.WARNING, "Connection lost, will attempt to reconnect", exception);
if (exception instanceof ConnectionLostException && disconnectCallback != null) {
disconnectCallback.run();
}
try {
Thread.sleep(10000);
connect();
} catch (InterruptedException e) {
logger.log(Level.WARNING, "Sleep interrupted", e);
}
}
}
Expand All @@ -268,7 +235,7 @@
WebSocketClient webSocketClient = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
try {
StompSession stompSession = stompClient.connect(webSocketConnectUrl, new StompSessionHandlerAdapter() {
StompSession stompSession = stompClient.connectAsync(webSocketConnectUrl, new StompSessionHandlerAdapter() {
@Override
public Type getPayloadType(StompHeaders headers) {
return super.getPayloadType(headers);
Expand All @@ -277,7 +244,7 @@
stompSession.disconnect();
return true;
} catch (Exception e) {
logger.log(Level.WARNING, "Remote service on " + webSocketConnectUrl + " does not support web socket connection", e);
logger.log(Level.WARNING, "Remote service on " + webSocketConnectUrl + " does not support web socket connection on url " + webSocketConnectUrl, e);

Check warning on line 247 in core/websocket/client/src/main/java/org/phoebus/core/websocket/client/WebSocketClientService.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Lambda should be used to defer string concatenation.

See more on https://sonarcloud.io/project/issues?id=ControlSystemStudio_phoebus&issues=AaAeGF-ClJkpUg2wFOsv&open=AaAeGF-ClJkpUg2wFOsv&pullRequest=3907
}
return false;
}
Expand Down
22 changes: 11 additions & 11 deletions dependencies/phoebus-target/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
</properties>

<profiles>
<!-- Overriding the default release profile to run the pre release script
with makes the required modification to .classpath and other manually maintained
<!-- Overriding the default release profile to run the pre release script
with makes the required modification to .classpath and other manually maintained
files -->
<profile>
<id>release</id>
Expand Down Expand Up @@ -60,7 +60,7 @@
</profiles>

<dependencies>
<!-- For testing. mockito-all contains hamcrest packages, resulting in
<!-- For testing. mockito-all contains hamcrest packages, resulting in
conflicts. This combination of junit, mockito, hamcrest seems to work -->
<dependency>
<groupId>org.phoebus</groupId>
Expand Down Expand Up @@ -481,10 +481,10 @@
</dependency>

<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.7.0</version>
</dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>

<dependency>
<groupId>javax.validation</groupId>
Expand All @@ -500,7 +500,7 @@
</dependency>

<!--JUL bindings for sfl4j-->
<dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.28</version>
Expand Down Expand Up @@ -551,7 +551,7 @@
<artifactId>epics-jackie-client</artifactId>
<version>3.1.0</version>
</dependency>

<!-- bouncycastle library used in PVA to decode and check OCSP confirmation of certificates
bcpkix depends on bcutil, which depends on bcprov
-->
Expand All @@ -570,7 +570,7 @@
<artifactId>bcprov-jdk18on</artifactId>
<version>1.84</version>
</dependency>

<!-- Lib to read/write MS Office (Excel, ..) files, fetching only POI itself -->
<dependency>
<groupId>org.apache.poi</groupId>
Expand Down Expand Up @@ -610,7 +610,7 @@
<artifactId>tika-core</artifactId>
<version>3.2.0</version>
</dependency>

<!-- ChartFX library for the Waterfall Plot widget -->
<dependency>
<groupId>io.fair-acc</groupId>
Expand Down
4 changes: 2 additions & 2 deletions dependencies/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
<profiles>
<profile>
<id>packageTarget</id>
<!-- Package the phoebus product with dependencies, run scripts, and
<!-- Package the phoebus product with dependencies, run scripts, and
sources -->
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<version>3.11.0</version>
<executions>
<execution>
<id>download-sources</id>
Expand Down
10 changes: 0 additions & 10 deletions misc/Dockerfile

This file was deleted.

Loading
Loading