Skip to content

Commit

Permalink
use java 15, refactor to use completion stages, add logging to disk
Browse files Browse the repository at this point in the history
  • Loading branch information
maxdeliso committed Feb 14, 2021
1 parent 3b6dd6a commit 066c6a2
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 153 deletions.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

<groupId>name.maxdeliso</groupId>
<artifactId>teflon</artifactId>
<version>1.0.7</version>
<version>1.0.8</version>
<packaging>jar</packaging>
<name>teflon</name>
<url>https://maxdeliso.name</url>

<properties>
<java.version>14</java.version>
<java.version>15</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
Expand Down Expand Up @@ -40,7 +40,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
Expand Down
140 changes: 80 additions & 60 deletions src/main/java/name/maxdeliso/teflon/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.TransferQueue;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import name.maxdeliso.teflon.ctx.RunContext;
import name.maxdeliso.teflon.data.JsonMessageMarshaller;
import name.maxdeliso.teflon.data.Message;
import name.maxdeliso.teflon.data.MessageMarshaller;
import name.maxdeliso.teflon.net.InterfaceChooser;
import name.maxdeliso.teflon.net.NetSelector;
import name.maxdeliso.teflon.ui.MainFrame;
import org.apache.logging.log4j.LogManager;
Expand All @@ -26,76 +28,94 @@ class Main {

private static final Gson GSON = new GsonBuilder().create();

private static final MessageMarshaller MM = new JsonMessageMarshaller(GSON);
private static final MessageMarshaller MESSAGE_MARSHALLER = new JsonMessageMarshaller(GSON);

private static final String MCAST_ADDR = "FF02:0:0:0:0:0:0:77";
private static final String MULTICAST_BIND_ADDRESS = "FF02:0:0:0:0:0:0:77";

private static final int UDP_PORT = 1337;

private static final int BUFFER_LENGTH = 4096;

private static final AtomicReference<MainFrame> mainFrameRef = new AtomicReference<>();
private static final TransferQueue<Message> TRANSFER_QUEUE = new LinkedTransferQueue<>();

private static final UUID UUID = java.util.UUID.randomUUID();

private static final CompletableFuture<MainFrame> MAIN_FRAME_F =
CompletableFuture
.supplyAsync(() -> new MainFrame(UUID, TRANSFER_QUEUE::add))
.thenApply(mainFrame -> {
mainFrame.setVisible(true);
return mainFrame;
});

private static final CompletableFuture<Optional<InetAddress>> BIND_F =
CompletableFuture
.supplyAsync(() ->
{
try {
var bindAddress = Inet6Address.getByName(MULTICAST_BIND_ADDRESS);
return Optional.ofNullable(bindAddress);
} catch (UnknownHostException uke) {
return Optional.empty();
}
}
);

private static final CompletableFuture<Optional<NetworkInterface>> INTERFACE_OPT_F =
CompletableFuture
.supplyAsync(InterfaceChooser::new)
.thenApply(interfaceChooser -> interfaceChooser.queryInterfaces().stream().findFirst());
private static final CompletableFuture<Void> MAIN =
BIND_F.thenCompose(inetAddressOpt -> INTERFACE_OPT_F.thenCompose(networkInterfaceOpt -> {
var inetAddress = inetAddressOpt
.orElseThrow(
() -> new IllegalStateException("failed to bind"));

var networkInterface = networkInterfaceOpt
.orElseThrow(
() -> new IllegalStateException("failed to locate viable network interface"));

return MAIN_FRAME_F
.thenCompose(mainFrame -> networkLoop(mainFrame, inetAddress, networkInterface));
}));

private static CompletableFuture<Void> networkLoop(
MainFrame mainFrame,
InetAddress bindAddress,
NetworkInterface networkInterface) {
return CompletableFuture.supplyAsync(() -> new NetSelector(UDP_PORT, BUFFER_LENGTH,
(_address, rxBytes) -> MESSAGE_MARSHALLER
.bufferToMessage(rxBytes)
.ifPresent(mainFrame::queueMessageDisplay),
bindAddress,
networkInterface,
() -> Optional
.ofNullable(TRANSFER_QUEUE.poll())
.map(MESSAGE_MARSHALLER::messageToBuffer)
.orElse(null)
)
).thenCompose(NetSelector::selectLoop);
}

public static void main(String[] args) {
try {
var firstViableInterface = Collections
.list(NetworkInterface.getNetworkInterfaces())
.stream()
.filter(ni -> {
try {
return ni.isUp() &&
ni.supportsMulticast() &&
!ni.isLoopback() &&
!ni.isPointToPoint() &&
!ni.isVirtual();
} catch (SocketException exc) {
LOG.error("failed to interrogate prospective network interface", exc);
return false;
}
})
.findFirst()
.orElseThrow();

var transferQueue = new LinkedTransferQueue<Message>();
var rc = new RunContext();
var bindAddr = InetAddress.getByName(MCAST_ADDR);

LOG.info("attempting bind to {} on interface {}", bindAddr, firstViableInterface);

mainFrameRef.set(new MainFrame(rc, transferQueue::add));

var netSelector = new NetSelector(UDP_PORT, BUFFER_LENGTH,
(_addr, rxBytes) -> MM
.bufferToMessage(rxBytes)
.ifPresent(msg -> mainFrameRef.get().queueMessageDisplay(msg)),
bindAddr,
firstViableInterface,
() -> Optional
.ofNullable(transferQueue.poll())
.map(MM::messageToBuffer)
.orElse(null)
);

mainFrameRef.get().setVisible(true);
netSelector.selectLoop();
mainFrameRef.get().dispose();
} catch (SocketException | UnknownHostException exc) {
var dialogFrame = new JFrame();
LOG.info("starting up with UUID {}", UUID);
MAIN.get();
} catch (InterruptedException ie) {
LOG.error("synchronization error", ie);
System.exit(1);
} catch (ExecutionException ee) {
LOG.error("error", ee.getCause());

var dialogFrame = new JFrame();
JOptionPane.showMessageDialog(
dialogFrame,
exc.getMessage(),
"Network Error",
ee.getCause().getMessage()
+ ". Please consult the log files for more information.",
"Error",
JOptionPane.ERROR_MESSAGE);

LOG.error("network related failure", exc);
dialogFrame.dispose();
} finally {
var mainFrame = mainFrameRef.get();

if(mainFrame != null) {
mainFrame.dispose();
}
System.exit(2);
}
}
}
15 changes: 0 additions & 15 deletions src/main/java/name/maxdeliso/teflon/ctx/RunContext.java

This file was deleted.

35 changes: 35 additions & 0 deletions src/main/java/name/maxdeliso/teflon/net/InterfaceChooser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package name.maxdeliso.teflon.net;

import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

public class InterfaceChooser {
private List<NetworkInterface> queryAvailableInterfaces() {
try {
return Collections.list(NetworkInterface.getNetworkInterfaces());
} catch (SocketException se) {
return new ArrayList<>();
}
}

public List<NetworkInterface> queryInterfaces() {
return queryAvailableInterfaces()
.stream()
.filter(ni -> {
try {
return ni.isUp() &&
ni.supportsMulticast() &&
!ni.isLoopback() &&
!ni.isPointToPoint() &&
!ni.isVirtual();
} catch (SocketException exc) {
return false;
}
})
.collect(Collectors.toList());
}
}
Loading

0 comments on commit 066c6a2

Please sign in to comment.