Skip to content

Commit

Permalink
fix invalid astm communication and general logging+comment improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
CalebSLane committed Jan 14, 2025
1 parent c8312c0 commit 220d34e
Show file tree
Hide file tree
Showing 19 changed files with 217 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@
import org.itech.ahb.lib.util.LogUtil;
import org.itech.ahb.lib.util.ThreadUtil;

//If this class gets too complicated, separate out the LISA-01 and E1382-95 protocols into separate classes
//If this class gets too complicated, separate out the
// LISA-01 and E1382-95 protocols into separate classes or separate sending from receiving.
// Also separate out the non-compliant transmission protocol into a separate class.
/**
* This class is a general communicator that can send and receive ASTM messages
* over protocols such as LIS01-A and E1382-95, as well as non-compliant transmissions
Expand Down Expand Up @@ -263,8 +265,8 @@ public Boolean establishmentReceive() throws IOException, InterruptedException {
}

/**
* Receives an ASTM message that is being sent non-compliantly (not using the ASTM transmission protocol).
* Instead the message is read character by character until the termination record is reached.
* Receives an ASTM message that is being sent non-compliantly (not using a proper ASTM transmission protocol).
* The non-compliant mode reads character by character until the termination record is reached.
*
* @return the received ASTM message.
* @throws ASTMCommunicationException if there is a communication error in the ASTM transmission protocol.
Expand Down Expand Up @@ -340,7 +342,7 @@ private ASTMMessage receiveInCompliantMode() throws IOException, ASTMCommunicati
try {
recievedFrameFuture.run();
ReadFrameInfo frameInfo = recievedFrameFuture.get(RECIEVE_FRAME_TIMEOUT, TimeUnit.SECONDS);
if (frameInfo.getStartChar() != EOT) {
if (frameInfo.getStartChar() == EOT) {
break;
}
Set<FrameError> frameErrors = frameInfo.getFrameErrors();
Expand Down Expand Up @@ -369,7 +371,7 @@ private ASTMMessage receiveInCompliantMode() throws IOException, ASTMCommunicati
if (exceptions.size() > MAX_FRAME_RETRY_ATTEMPTS) {
log.error("MAX_FRAME_RETRY_ATTEMPTS reached for frame");
for (Exception e : exceptions) {
log.error(e.getMessage());
log.error("" + e.getMessage());
}
//sender is supposed to enter the termination phase when max attempts are reached, which means EOT is expected, but is irrelevant)
try {
Expand Down Expand Up @@ -451,9 +453,11 @@ private Set<FrameError> readNextCompliantFrame(List<ASTMFrame> frames, int expec
while (curChar != ETB && curChar != ETX) {
if (RESTRICTED_CHARACTERS.contains(curChar)) {
frameErrors.add(FrameError.ILLEGAL_CHAR);
log.error("illegal character detected: '" + LogUtil.convertForDisplay(curChar) + "'.");
}
if ((astmVersion == ASTMVersion.LIS01_A ? MAX_TEXT_SIZE : MAX_TEXT_SIZE_E138195) < frameSize) {
frameErrors.add(FrameError.MAX_SIZE_EXCEEDED);
log.error("max frame size exceeded character.");
}
textBuilder.append(curChar);
++frameSize;
Expand Down Expand Up @@ -510,6 +514,8 @@ private Set<FrameError> readNextCompliantFrame(List<ASTMFrame> frames, int expec
frame.setText(text);
frames.add(frame);
log.debug("frame added to list of frames");
} else {
log.debug("frame not added to list of frames due to errors: " + frameErrors);
}
if (Thread.interrupted()) {
throw new InterruptedException();
Expand Down Expand Up @@ -580,7 +586,7 @@ public SendResult sendProtocol(ASTMMessage message)
}

if (established) {
log.trace("sendProtocol: established");
log.trace("established");
} else if (nakReceived) {
return new SendResult(false, true);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ public DefaultASTMMessage() {}
* @param message the message to parse into a series of ASTM records that will make up the message.
*/
public DefaultASTMMessage(String message) {
records = Arrays.stream(message.split("((?<=\\n))"))
.map(e -> new DefaultASTMRecord(e))
.collect(Collectors.toList());
records = Arrays.stream(message.split("\\r?\\n")).map(e -> new DefaultASTMRecord(e)).collect(Collectors.toList());
}

/**
Expand Down Expand Up @@ -62,6 +60,9 @@ public void addRecord(ASTMRecord record) {

@Override
public List<ASTMRecord> getRecords() {
if (records == null) {
return null;
}
return Collections.unmodifiableList(records);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public ASTMHandlerServiceResponse handle(ASTMMessage message) {
public ASTMHandlerServiceResponse handle(ASTMMessage message, Set<ASTMForwardingHandlerInfo> handlersInfos) {
Map<ASTMMessage, List<ASTMHandler>> messageHandlersMap = new HashMap<>();
log.debug("finding a handler for astm message: " + message.hashCode());
log.trace("message: '" + message.getMessage() + "'");
for (ASTMHandler handler : handlers) {
if (handler.matches(message)) {
log.debug("handler: '" + handler.getName() + "' found for astm message: " + message.hashCode());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public DefaultForwardingASTMToHTTPHandler(URI forwardingUri, String username, ch
public ASTMHandlerResponse handle(ASTMMessage message) {
HttpClient client = HttpClient.newHttpClient();
log.debug("creating request to forward to http server at " + forwardingUri.toString());
log.trace("request: '" + message.getMessage() + "'");
Builder requestBuilder = HttpRequest.newBuilder() //
.uri(forwardingUri) //
.POST(HttpRequest.BodyPublishers.ofString(message.getMessage())); //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@ public interface ASTMInterpreter {
*/
ASTMMessage interpretFramesToASTMMessage(List<ASTMFrame> frames) throws FrameParsingException;

/**
* Interprets an ASTM record as a list of ASTM frames.
*
* @param record the ASTM record.
* @return the list of ASTM frames that would make up this record for transmission.
*/
List<ASTMFrame> interpretASTMRecordsToFrames(ASTMRecord record);

/**
* Interprets an ASTM message as a list of ASTM frames.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,27 +55,29 @@ public ASTMMessage interpretFramesToASTMMessage(List<ASTMFrame> frames) throws F
* @return true if the frame contains a message terminator, false otherwise.
*/
private boolean frameContainsMessageTerminator(ASTMFrame frame) {
log.trace("checking if frame contains message terminator...");
String[] lines = frame.getText().split(RECORD_SEPERATOR);
if (lines[lines.length - 1].startsWith(MESSAGE_TERMINATOR_RECORD_START)) {
log.trace("frame contains message terminator");
return true;
}
log.trace("frame does NOT contain message terminator");
return false;
}

@Override
public List<ASTMFrame> interpretASTMRecordsToFrames(ASTMRecord record) {
private List<ASTMFrame> interpretASTMRecordsToFrames(ASTMRecord record, int recordNumber) {
log.debug("interpreting astm record as frames...");
List<ASTMFrame> frames = new ArrayList<>();
String[] frameTexts = record.getRecord().split("(?<=\\G.{" + GeneralASTMCommunicator.MAX_TEXT_SIZE + "})");
log.trace("astm record: " + record);
for (int i = 0; i < frameTexts.length; i++) {
ASTMFrame curFrame = new DefaultASTMFrame();
curFrame.setText(frameTexts[i]);
curFrame.setFrameNumber((i + 1) % 8);
curFrame.setType(i != (frameTexts.length - 1) ? FrameType.INTERMEDIATE : FrameType.END);
frames.add(curFrame);
log.trace(curFrame.toString());
}
log.trace("record was interpreted across " + frames.size() + " frames");
log.debug("finished interpreting astm record as frames");
return frames;
}
Expand All @@ -85,9 +87,15 @@ public List<ASTMFrame> interpretASTMMessageToFrames(ASTMMessage message) {
log.debug("interpreting astm messages as frames...");
List<ASTMFrame> frames = new ArrayList<>();
log.trace("astm message: " + message.getMessage());
for (ASTMRecord record : message.getRecords()) {
frames.addAll(interpretASTMRecordsToFrames(record));
for (int i = 0; i < message.getRecords().size(); i++) {
frames.addAll(interpretASTMRecordsToFrames(message.getRecords().get(i), i));
}
for (int i = 0; i < frames.size(); i++) {
frames.get(i).setFrameNumber((i + 1) % 8);
}

log.trace("message was interpreted across " + frames.size() + " frames");
log.debug("finished interpreting astm message as frames");
return frames;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public HTTPHandlerResponse handle(ASTMMessage message, Set<HTTPHandlerInfo> hand
* @return the HTTP handler response.
*/
private HTTPHandlerResponse handle(ASTMMessage message, Set<HTTPHandlerInfo> handlerInfos, int retryAttempt) {
log.trace(this.getName() + ": " + this.hashCode() + " " + "retry attempt: " + retryAttempt);
Socket socket = null;
Communicator communicator = null;
String forwardingAddress = this.defaultForwardingAddress;
Expand All @@ -99,7 +100,7 @@ private HTTPHandlerResponse handle(ASTMMessage message, Set<HTTPHandlerInfo> han
}
}
try {
if (retryAttempt > 0) {
if (retryAttempt > 0 && retryAttempt <= MAX_FORWARD_RETRY_ATTEMPTS) {
log.debug("waiting to reattempt sending to astm server...");
Thread.sleep(SEND_ATTEMPTS_WAIT * 1000);
log.debug("reattempting forward to astm server...");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public HTTPHandlerServiceResponse handle(ASTMMessage message, Set<HTTPForwarding

messageHandlersMap.put(message, matchingMessageHandlers);
if (mode == Mode.FIRST) {
log.debug("marshall mode is FIRST, proceeding with a single handler");
log.debug("mode is FIRST, proceeding with a single handler");
break;
}
}
Expand All @@ -87,6 +87,7 @@ public HTTPHandlerServiceResponse handle(ASTMMessage message, Set<HTTPForwarding
log.debug("handling astm http message...");
for (Entry<ASTMMessage, List<HTTPHandler>> matchingMessageHandlers : messageHandlersMap.entrySet()) {
for (HTTPHandler messageHandler : matchingMessageHandlers.getValue()) {
log.trace("messageHandler astm http message...");
try {
HTTPHandlerResponse handleResponse = messageHandler.handle(
matchingMessageHandlers.getKey(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.itech.ahb.lib.common.handling.HandlerServiceResponse;

/**
* This class represents the response from an HTTP handler service. ie. a collection of responses from individual hadlers.
* This class represents the response from an HTTP handler service. ie. a collection of responses from individual handlers.
*/
@Data
@AllArgsConstructor
Expand Down
45 changes: 39 additions & 6 deletions src/main/java/org/itech/ahb/AstmHttpBridgeApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import org.itech.ahb.config.properties.ASTMLIS1AListenServerConfigurationProperties;
import org.itech.ahb.config.properties.HTTPForwardServerConfigurationProperties;
import org.itech.ahb.lib.astm.handling.ASTMHandler;
import org.itech.ahb.lib.astm.handling.ASTMHandlerMarshaller;
import org.itech.ahb.lib.astm.handling.ASTMHandlerMarshaller.MarshallerMode;
import org.itech.ahb.lib.astm.handling.ASTMHandlerService;
import org.itech.ahb.lib.astm.handling.ASTMHandlerService.Mode;
import org.itech.ahb.lib.astm.handling.DefaultForwardingASTMToHTTPHandler;
import org.itech.ahb.lib.astm.interpretation.ASTMInterpreterFactory;
import org.itech.ahb.lib.astm.interpretation.DefaultASTMInterpreterFactory;
Expand All @@ -23,6 +23,9 @@
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.util.StringUtils;

/**
* Main application class for the ASTM HTTP Bridge. Starts the Spring Boot project and defines beans for the project.
*/
@SpringBootApplication
@ConfigurationPropertiesScan
@EnableAsync
Expand All @@ -34,17 +37,33 @@
@Slf4j
public class AstmHttpBridgeApplication {

/**
* Main method to run the application.
*
* @param args the command line arguments
*/
public static void main(String[] args) {
SpringApplication.run(AstmHttpBridgeApplication.class, args);
}

/**
* Bean for creating an ASTM interpreter factory.
*
* @return the ASTM interpreter factory
*/
@Bean
public ASTMInterpreterFactory astmInterpreterFactory() {
return new DefaultASTMInterpreterFactory();
}

/**
* Bean for creating an ASTM handler service.
*
* @param httpForwardConfig the HTTP forward server configuration properties
* @return the ASTM handler service
*/
@Bean
public ASTMHandlerMarshaller astmHandlerMarshaller(HTTPForwardServerConfigurationProperties httpForwardConfig) {
public ASTMHandlerService astmHandlerService(HTTPForwardServerConfigurationProperties httpForwardConfig) {
List<ASTMHandler> astmHandlers;
if (StringUtils.hasText(httpForwardConfig.getUsername())) {
astmHandlers = Arrays.asList(
Expand All @@ -57,23 +76,37 @@ public ASTMHandlerMarshaller astmHandlerMarshaller(HTTPForwardServerConfiguratio
} else {
astmHandlers = Arrays.asList(new DefaultForwardingASTMToHTTPHandler(httpForwardConfig.getUri()));
}
return new ASTMHandlerMarshaller(astmHandlers, MarshallerMode.FIRST);
return new ASTMHandlerService(astmHandlers, Mode.FIRST);
}

/**
* Bean for creating an ASTM servlet for LIS1-A.
*
* @param astmListenConfig the ASTM listen server configuration properties
* @param httpForwardConfig the HTTP forward server configuration properties
* @return the ASTM servlet
*/
@Bean
public ASTMServlet astmLIS01AServlet(
ASTMLIS1AListenServerConfigurationProperties astmListenConfig,
HTTPForwardServerConfigurationProperties httpForwardConfig
) {
log.info("creating astm server bean to handle incoming astm LIS1-A requests on port " + astmListenConfig.getPort());
return new ASTMServlet(
astmHandlerMarshaller(httpForwardConfig),
astmHandlerService(httpForwardConfig),
astmInterpreterFactory(),
astmListenConfig.getPort(),
ASTMVersion.LIS01_A
);
}

/**
* Bean for creating an ASTM servlet for E1381-95.
*
* @param astmListenConfig the ASTM listen server configuration properties
* @param httpForwardConfig the HTTP forward server configuration properties
* @return the ASTM servlet
*/
@Bean
public ASTMServlet astmE138195Servlet(
ASTME138195ListenServerConfigurationProperties astmListenConfig,
Expand All @@ -83,7 +116,7 @@ public ASTMServlet astmE138195Servlet(
"creating astm 1381-95 server bean to handle incoming astm 1381-95 requests on port " + astmListenConfig.getPort()
);
return new ASTMServlet(
astmHandlerMarshaller(httpForwardConfig),
astmHandlerService(httpForwardConfig),
astmInterpreterFactory(),
astmListenConfig.getPort(),
ASTMVersion.E1381_95
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/itech/ahb/config/YamlPropertySourceFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,19 @@
import org.springframework.core.io.support.EncodedResource;
import org.springframework.core.io.support.PropertySourceFactory;

/**
* Factory for interpreting YAML files as a property source for Spring Boot.
*/
public class YamlPropertySourceFactory implements PropertySourceFactory {

/**
* Creates a property source from the given encoded resource.
*
* @param name the name of the property source
* @param encodedResource the encoded resource
* @return the property source
* @throws IOException if an I/O error occurs accessing the file
*/
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,16 @@
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* Configuration properties for the ASTM E1381-95 listen server which listens for ASTM messages compliant with E1381-95.
* Then it forwards the ASTM messages to the configured HTTP server.
*/
@ConfigurationProperties(prefix = "org.itech.ahb.listen-astm-server.e1381-95")
@Data
public class ASTME138195ListenServerConfigurationProperties {

/**
* The port on which the server listens.
*/
private int port = 12011;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* Configuration properties for the ASTM server that this application should forward to when it receives
* an ASTM message over HTTP(S). This is used to forward the ASTM messages to a server like an alayzer device that understands an ASTM transmission protocol.
*/
@ConfigurationProperties(prefix = "org.itech.ahb.forward-astm-server")
@Data
public class ASTMForwardServerConfigurationProperties {

/**
* The hostname of the forward server.
*/
private String hostName = "localhost";

/**
* The port on which the forward server listens.
*/
private int port = 12001;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

//LIS1A is equivalent to E1381-02
/**
* Configuration properties for the ASTM LIS1-A listen server which listens for ASTM messages compliant with LIS1A.
* Then it forwards the ASTM messages to the configured HTTP server.
* LIS1A is equivalent to E1381-02.
*/
@ConfigurationProperties(prefix = "org.itech.ahb.listen-astm-server")
@Data
public class ASTMLIS1AListenServerConfigurationProperties {

/**
* The port on which the server listens.
*/
private int port = 12001;
}
Loading

0 comments on commit 220d34e

Please sign in to comment.