Skip to content

Commit a2cbfeb

Browse files
committed
feat: Added the API module for the new transports.
1 parent 4602b8a commit a2cbfeb

22 files changed

Lines changed: 2569 additions & 0 deletions

plc4j/transports/api/pom.xml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
https://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing,
14+
software distributed under the License is distributed on an
15+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
KIND, either express or implied. See the License for the
17+
specific language governing permissions and limitations
18+
under the License.
19+
-->
20+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
21+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
22+
<modelVersion>4.0.0</modelVersion>
23+
24+
<parent>
25+
<groupId>org.apache.plc4x</groupId>
26+
<artifactId>plc4j-transports</artifactId>
27+
<version>0.14.0-SNAPSHOT</version>
28+
</parent>
29+
30+
<artifactId>plc4j-transports-api</artifactId>
31+
32+
<name>PLC4J: Transports: API</name>
33+
34+
<properties>
35+
<project.build.outputTimestamp>2024-02-16T14:53:02Z</project.build.outputTimestamp>
36+
</properties>
37+
38+
<dependencies>
39+
<!-- PLC4J API -->
40+
<dependency>
41+
<groupId>org.apache.plc4x</groupId>
42+
<artifactId>plc4j-api</artifactId>
43+
<version>0.14.0-SNAPSHOT</version>
44+
</dependency>
45+
46+
<dependency>
47+
<groupId>org.apache.plc4x</groupId>
48+
<artifactId>plc4j-spi-config</artifactId>
49+
<version>0.14.0-SNAPSHOT</version>
50+
</dependency>
51+
<dependency>
52+
<groupId>org.apache.plc4x</groupId>
53+
<artifactId>plc4j-utils-audit-log-api</artifactId>
54+
<version>0.14.0-SNAPSHOT</version>
55+
</dependency>
56+
57+
<!-- Logging -->
58+
<dependency>
59+
<groupId>org.slf4j</groupId>
60+
<artifactId>slf4j-api</artifactId>
61+
</dependency>
62+
63+
<!-- OSGi Dependencies -->
64+
<dependency>
65+
<groupId>org.osgi</groupId>
66+
<artifactId>org.osgi.service.component.annotations</artifactId>
67+
</dependency>
68+
69+
<!-- Test Dependencies -->
70+
<dependency>
71+
<groupId>org.mockito</groupId>
72+
<artifactId>mockito-core</artifactId>
73+
<scope>test</scope>
74+
</dependency>
75+
<dependency>
76+
<groupId>ch.qos.logback</groupId>
77+
<artifactId>logback-classic</artifactId>
78+
<scope>test</scope>
79+
</dependency>
80+
<dependency>
81+
<groupId>org.junit.jupiter</groupId>
82+
<artifactId>junit-jupiter-api</artifactId>
83+
<scope>test</scope>
84+
</dependency>
85+
</dependencies>
86+
87+
</project>
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.plc4x.java.spi.transports.api;
21+
22+
import org.apache.plc4x.java.spi.transports.api.config.TransportConfiguration;
23+
24+
import java.util.function.Consumer;
25+
26+
/**
27+
* Extension of TransportInstance that supports asynchronous, event-driven I/O.
28+
* This eliminates the need for polling loops.
29+
* <p>
30+
* Implementations register with the underlying I/O mechanism (e.g., NIO Selector)
31+
* and notify registered listeners when data becomes available, enabling zero-polling,
32+
* event-driven architectures.
33+
*
34+
* @param <T> the configuration type
35+
*/
36+
public interface AsyncTransportInstance<T extends TransportConfiguration> extends TransportInstance<T> {
37+
38+
/**
39+
* Registers a listener that will be called when data becomes available.
40+
* This allows for event-driven architectures without polling.
41+
* <p>
42+
* The transport implementation is responsible for monitoring the underlying
43+
* I/O channel and invoking this listener when data arrives.
44+
*
45+
* @param listener callback invoked when data is available
46+
*/
47+
void registerDataListener(Runnable listener);
48+
49+
/**
50+
* Removes the data available listener, stopping event notifications.
51+
*/
52+
void removeDataListener();
53+
54+
/**
55+
* Registers a listener that will be called when the transport is disconnected.
56+
* <p>
57+
* This is critical for properly handling connection failures - any pending
58+
* operations waiting for responses should be completed exceptionally when
59+
* the transport dies, rather than waiting for a timeout.
60+
* <p>
61+
* The listener receives the exception that caused the disconnect, or null
62+
* if the disconnect was graceful (e.g., the remote side closed normally).
63+
*
64+
* @param listener callback invoked when the transport disconnects
65+
*/
66+
default void registerDisconnectListener(Consumer<Throwable> listener) {
67+
// Default no-op for backward compatibility
68+
}
69+
70+
/**
71+
* Removes the disconnect listener.
72+
*/
73+
default void removeDisconnectListener() {
74+
// Default no-op for backward compatibility
75+
}
76+
77+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.plc4x.java.spi.transports.api;
21+
22+
import org.apache.plc4x.java.spi.transports.api.config.TransportConfiguration;
23+
import org.apache.plc4x.java.utils.auditlog.api.AuditLog;
24+
import org.apache.plc4x.java.utils.auditlog.api.AuditLogEventType;
25+
26+
import java.util.Objects;
27+
28+
public abstract class BaseTransportInstance<T extends TransportConfiguration> implements TransportInstance<T> {
29+
30+
private final T transportConfig;
31+
private final AuditLog auditLog;
32+
33+
public BaseTransportInstance(T transportConfig, AuditLog auditLog) {
34+
this.transportConfig = Objects.requireNonNull(transportConfig);
35+
this.auditLog = auditLog;
36+
auditLog.write(AuditLogEventType.SYSTEM, "Creating Transport with config", transportConfig);
37+
}
38+
39+
public T getConfiguration() {
40+
return transportConfig;
41+
}
42+
43+
public AuditLog getAuditLog() {
44+
return auditLog;
45+
}
46+
47+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.plc4x.java.spi.transports.api;
21+
22+
import org.apache.plc4x.java.spi.transports.api.config.TransportConfiguration;
23+
import org.apache.plc4x.java.spi.transports.api.exceptions.TransportException;
24+
25+
import java.time.Duration;
26+
import java.util.concurrent.TimeoutException;
27+
28+
/**
29+
* Extension of TransportInstance that supports blocking reads with timeout.
30+
* This allows the receive loop to block until data is available, eliminating
31+
* the need for polling with Thread.sleep().
32+
*
33+
* @param <T> the configuration type
34+
*/
35+
public interface BlockingTransportInstance<T extends TransportConfiguration> extends TransportInstance<T> {
36+
37+
/**
38+
* Blocks until at least one byte is available or the timeout expires.
39+
* This method eliminates the need for polling loops with Thread.sleep().
40+
*
41+
* @param timeout maximum time to wait for data
42+
* @throws TransportException if an I/O error occurs
43+
* @throws TimeoutException if the timeout expires before data is available
44+
* @throws InterruptedException if the thread is interrupted while waiting
45+
*/
46+
void waitForData(Duration timeout) throws TransportException, TimeoutException, InterruptedException;
47+
48+
/**
49+
* Blocks until the specified number of bytes are available or the timeout expires.
50+
*
51+
* @param numBytes minimum number of bytes to wait for
52+
* @param timeout maximum time to wait
53+
* @throws TransportException if an I/O error occurs
54+
* @throws TimeoutException if the timeout expires
55+
* @throws InterruptedException if the thread is interrupted while waiting
56+
*/
57+
default void waitForBytes(int numBytes, Duration timeout) throws TransportException, TimeoutException, InterruptedException {
58+
long deadline = System.nanoTime() + timeout.toNanos();
59+
while (getNumBytesAvailable() < numBytes) {
60+
long remaining = deadline - System.nanoTime();
61+
if (remaining <= 0) {
62+
throw new TimeoutException("Timeout waiting for " + numBytes + " bytes");
63+
}
64+
waitForData(Duration.ofNanos(remaining));
65+
}
66+
}
67+
68+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.plc4x.java.spi.transports.api;
21+
22+
import org.slf4j.Logger;
23+
import org.slf4j.LoggerFactory;
24+
25+
import java.util.HashMap;
26+
import java.util.Map;
27+
import java.util.Optional;
28+
import java.util.ServiceLoader;
29+
30+
public class DefaultTransportManager implements TransportManager {
31+
32+
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTransportManager.class);
33+
34+
protected final ClassLoader classLoader;
35+
36+
private final Map<String, Transport> transportMap;
37+
38+
public DefaultTransportManager() {
39+
this(Thread.currentThread().getContextClassLoader());
40+
}
41+
42+
public DefaultTransportManager(ClassLoader classLoader) {
43+
LOGGER.info("Instantiating new Transport Manager with class loader {}", classLoader);
44+
this.classLoader = classLoader;
45+
transportMap = new HashMap<>();
46+
ServiceLoader<Transport> transportLoader = ServiceLoader.load(Transport.class, classLoader);
47+
LOGGER.info("Registering available transports...");
48+
for (Transport transport : transportLoader) {
49+
if (transportMap.containsKey(transport.getTransportCode())) {
50+
throw new IllegalStateException(
51+
"Multiple transport implementations available for transport code '" +
52+
transport.getTransportName() + "'");
53+
}
54+
LOGGER.info("Registering transport {} ({})", transport.getTransportCode(), transport.getTransportName());
55+
transportMap.put(transport.getTransportCode(), transport);
56+
}
57+
}
58+
59+
@Override
60+
public Optional<Transport> getTransport(String transportCode) {
61+
return Optional.ofNullable(transportMap.get(transportCode));
62+
}
63+
64+
}

0 commit comments

Comments
 (0)