Re-factor Listeners and Resolvers to use ServiceLoader protocol discovery

This commit is contained in:
Travis Burtrum 2019-03-13 00:01:48 -04:00
parent 0bca5ee0a2
commit f118f6410b
14 changed files with 128 additions and 61 deletions

View File

@ -0,0 +1,19 @@
package com.moparisthebest.dns.listen;
import com.moparisthebest.dns.net.ParsedUrl;
import com.moparisthebest.dns.resolve.Resolver;
import java.util.concurrent.ExecutorService;
public class DefaultServices implements Services {
@Override
public Listener getListener(ParsedUrl parsedUrl, final Resolver resolver, final ExecutorService executor) {
switch(parsedUrl.getProtocol()) {
case "tcp":
return new TcpAsync(parsedUrl.getAddr(), resolver, executor);
case "udp":
return new UdpSync(parsedUrl.getAddr(), resolver, executor);
}
return null;
}
}

View File

@ -6,30 +6,21 @@ import com.moparisthebest.dns.resolve.Resolver;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.Arrays; import java.util.Arrays;
import java.util.ServiceLoader;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
public interface Listener extends Runnable, AutoCloseable { public interface Listener extends Runnable, AutoCloseable {
public static Listener of(final String listener, final Resolver resolver, final ExecutorService executor) {
/* ServiceLoader<Services> services = ServiceLoader.load(Services.class);
listener = listener.trim().toLowerCase();
final String[] hostPort = listener.substring(6).split(":"); static Listener of(final String listener, final Resolver resolver, final ExecutorService executor) {
//System.out.println("hostPort: " + Arrays.toString(hostPort));
final SocketAddress socketAddress = new InetSocketAddress(hostPort[0], Integer.parseInt(hostPort[1]));
//System.out.println("socketAddress: " + socketAddress);
if(listener.startsWith("tcp://")) {
return new TcpAsync(socketAddress, resolver, executor);
} else if(listener.startsWith("udp://")) {
return new UdpSync(socketAddress, resolver, executor);
}
*/
final ParsedUrl parsedUrl = ParsedUrl.of(listener); final ParsedUrl parsedUrl = ParsedUrl.of(listener);
switch(parsedUrl.getProtocol()) { for (final Services s : services) {
case "tcp": final Listener ret = s.getListener(parsedUrl, resolver, executor);
return new TcpAsync(parsedUrl.getAddr(), resolver, executor); if (ret != null)
case "udp": return ret;
return new UdpSync(parsedUrl.getAddr(), resolver, executor);
} }
throw new IllegalArgumentException("invalid listener format"); throw new IllegalArgumentException("unhandled listener format: " + listener);
} }
public static Listener ofAndStart(final String listener, final Resolver resolver, final ExecutorService executor) { public static Listener ofAndStart(final String listener, final Resolver resolver, final ExecutorService executor) {

View File

@ -0,0 +1,10 @@
package com.moparisthebest.dns.listen;
import com.moparisthebest.dns.net.ParsedUrl;
import com.moparisthebest.dns.resolve.Resolver;
import java.util.concurrent.ExecutorService;
public interface Services {
Listener getListener(ParsedUrl parsedUrl, Resolver resolver, ExecutorService executor);
}

View File

@ -11,13 +11,15 @@ import java.util.*;
public class ParsedUrl { public class ParsedUrl {
private final String urlStr;
private final SocketAddress addr; private final SocketAddress addr;
private final URI uri; // minus # private final URI uri; // minus #
private final Map<String, String> props; // after #, split by ; private final Map<String, String> props; // after #, split by ;
private final Proxy proxy; private final Proxy proxy;
private final SSLSocketFactory sslSocketFactory; private final SSLSocketFactory sslSocketFactory;
public ParsedUrl(final SocketAddress addr, final URI uri, final Map<String, String> props, final Proxy proxy, final SSLSocketFactory sslSocketFactory) { public ParsedUrl(final String urlStr, final SocketAddress addr, final URI uri, final Map<String, String> props, final Proxy proxy, final SSLSocketFactory sslSocketFactory) {
this.urlStr = urlStr;
this.addr = addr; this.addr = addr;
this.uri = uri; this.uri = uri;
this.props = props; this.props = props;
@ -70,7 +72,7 @@ public class ParsedUrl {
} }
if(sslSocketFactory == null && url.getScheme().equals("tls")) if(sslSocketFactory == null && url.getScheme().equals("tls"))
sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
return new ParsedUrl(addr, url, props, proxy, sslSocketFactory); return new ParsedUrl(urlStr, addr, url, props, proxy, sslSocketFactory);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof RuntimeException) if (e instanceof RuntimeException)
throw (RuntimeException) e; throw (RuntimeException) e;
@ -104,6 +106,10 @@ public class ParsedUrl {
return toUrlRemoveRef(uri); return toUrlRemoveRef(uri);
} }
public String getUrlStr() {
return urlStr;
}
public SocketAddress getAddr() { public SocketAddress getAddr() {
return addr; return addr;
} }

View File

@ -29,17 +29,6 @@ public abstract class AbstractQueueProcessingResolver implements QueueProcessing
executor.execute(this); executor.execute(this);
} }
@Override
public <E extends RequestResponse> CompletableFuture<E> resolveAsync(final E requestResponse) {
return null;
/*
return CompletableFuture.supplyAsync(() -> {
requestResponse.setResponse(resolve(requestResponse.getRequest()));
return requestResponse;
}, executor);
*/
}
@Override @Override
public void run() { public void run() {
thisThread = Thread.currentThread(); thisThread = Thread.currentThread();

View File

@ -0,0 +1,19 @@
package com.moparisthebest.dns.resolve;
import com.moparisthebest.dns.net.ParsedUrl;
public class DefaultServices implements Services {
@Override
public Resolver getResolver(ParsedUrl parsedUrl) {
final int connectTimeout = Integer.parseInt(parsedUrl.getProps().getOrDefault("connectTimeout", "500"));
switch(parsedUrl.getProtocol()) {
case "tcp":
case "tls":
return new SocketResolver(parsedUrl.getAddr(), connectTimeout, parsedUrl.getProxy(), parsedUrl.getSslSocketFactory());
case "http":
case "https":
return new HttpResolver(parsedUrl.getUrlWithoutFragment(), connectTimeout, parsedUrl.getProxy(), parsedUrl.getSslSocketFactory());
}
return null;
}
}

View File

@ -0,0 +1,25 @@
package com.moparisthebest.dns.resolve;
import com.moparisthebest.dns.dto.Packet;
import java.util.concurrent.CompletableFuture;
public class DelegatingQueueProcessingResolver extends AbstractQueueProcessingResolver {
private final Resolver delegate;
public DelegatingQueueProcessingResolver(final int maxRetries, final String name, final Resolver delegate) {
super(maxRetries, name);
this.delegate = delegate;
}
@Override
public <E extends RequestResponse> CompletableFuture<E> resolveAsync(final E requestResponse) {
return delegate.resolveAsync(requestResponse);
}
@Override
public Packet resolve(final Packet request) throws Exception {
return delegate.resolve(request);
}
}

View File

@ -9,7 +9,7 @@ import java.net.*;
import static com.moparisthebest.dns.Util.readPacket; import static com.moparisthebest.dns.Util.readPacket;
public class HttpResolver extends AbstractQueueProcessingResolver { public class HttpResolver implements Resolver {
private final OpenConnection openConnection; private final OpenConnection openConnection;
private final int connectTimeout; private final int connectTimeout;
private final int readTimeout = 4000; private final int readTimeout = 4000;
@ -18,8 +18,7 @@ public class HttpResolver extends AbstractQueueProcessingResolver {
HttpURLConnection open() throws Exception; HttpURLConnection open() throws Exception;
} }
public HttpResolver(final int maxRetries, final String name, final URL url, final int connectTimeout, final Proxy proxy, final SSLSocketFactory sslSocketFactory) { public HttpResolver(final URL url, final int connectTimeout, final Proxy proxy, final SSLSocketFactory sslSocketFactory) {
super(maxRetries, name);
this.connectTimeout = connectTimeout; this.connectTimeout = connectTimeout;
if(proxy == null && sslSocketFactory == null) { if(proxy == null && sslSocketFactory == null) {
openConnection = () -> (HttpURLConnection) url.openConnection(); openConnection = () -> (HttpURLConnection) url.openConnection();

View File

@ -1,14 +1,9 @@
package com.moparisthebest.dns.resolve; package com.moparisthebest.dns.resolve;
import com.moparisthebest.dns.dto.Packet;
import com.moparisthebest.dns.net.ParsedUrl; import com.moparisthebest.dns.net.ParsedUrl;
import javax.net.SocketFactory;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Map; import java.util.Map;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
public interface QueueProcessingResolver extends Resolver, Runnable, AutoCloseable { public interface QueueProcessingResolver extends Resolver, Runnable, AutoCloseable {
@ -27,15 +22,6 @@ public interface QueueProcessingResolver extends Resolver, Runnable, AutoCloseab
String name = parsedUrl.getProps().get("name"); String name = parsedUrl.getProps().get("name");
if(name == null) if(name == null)
name = parsedUrl.getUri().toString(); name = parsedUrl.getUri().toString();
final int connectTimeout = Integer.parseInt(parsedUrl.getProps().getOrDefault("connectTimeout", "500")); return new DelegatingQueueProcessingResolver(maxRetries, name, Resolver.of(parsedUrl));
switch(parsedUrl.getProtocol()) {
case "tcp":
case "tls":
return new SocketResolver(maxRetries, name, parsedUrl.getAddr(), connectTimeout, parsedUrl.getProxy(), parsedUrl.getSslSocketFactory());
case "http":
case "https":
return new HttpResolver(maxRetries, name, parsedUrl.getUrlWithoutFragment(), connectTimeout, parsedUrl.getProxy(), parsedUrl.getSslSocketFactory());
}
throw new IllegalArgumentException("invalid listener format");
} }
} }

View File

@ -1,17 +1,32 @@
package com.moparisthebest.dns.resolve; package com.moparisthebest.dns.resolve;
import com.moparisthebest.dns.dto.Packet; import com.moparisthebest.dns.dto.Packet;
import com.moparisthebest.dns.listen.Listener; import com.moparisthebest.dns.net.ParsedUrl;
import com.moparisthebest.dns.listen.TcpAsync;
import com.moparisthebest.dns.listen.UdpSync;
import javax.net.SocketFactory; import java.util.ServiceLoader;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
public interface Resolver { public interface Resolver {
<E extends RequestResponse> CompletableFuture<E> resolveAsync(E requestResponse); default <E extends RequestResponse> CompletableFuture<E> resolveAsync(E requestResponse) {
return null;
/*
return CompletableFuture.supplyAsync(() -> {
requestResponse.setResponse(resolve(requestResponse.getRequest()));
return requestResponse;
}, executor);
*/
}
Packet resolve(Packet request) throws Exception; Packet resolve(Packet request) throws Exception;
ServiceLoader<Services> services = ServiceLoader.load(Services.class);
static Resolver of(final ParsedUrl parsedUrl) {
for (final Services s : services) {
final Resolver ret = s.getResolver(parsedUrl);
if (ret != null)
return ret;
}
throw new IllegalArgumentException("unhandled resolver format: " + parsedUrl.getUrlStr());
}
} }

View File

@ -0,0 +1,7 @@
package com.moparisthebest.dns.resolve;
import com.moparisthebest.dns.net.ParsedUrl;
public interface Services {
Resolver getResolver(ParsedUrl parsedUrl);
}

View File

@ -14,7 +14,7 @@ import static com.moparisthebest.dns.Util.readTcpPacket;
import static com.moparisthebest.dns.Util.tryClose; import static com.moparisthebest.dns.Util.tryClose;
import static com.moparisthebest.dns.Util.writeTcpPacket; import static com.moparisthebest.dns.Util.writeTcpPacket;
public class SocketResolver extends AbstractQueueProcessingResolver { public class SocketResolver implements Resolver {
private final OpenSocket openConnection; private final OpenSocket openConnection;
private final int readTimeout = 4000; private final int readTimeout = 4000;
@ -22,8 +22,7 @@ public class SocketResolver extends AbstractQueueProcessingResolver {
Socket open() throws Exception; Socket open() throws Exception;
} }
public SocketResolver(final int maxRetries, final String name, final SocketAddress endpoint, final int connectTimeout, final Proxy proxy, final SSLSocketFactory sslSocketFactory) { public SocketResolver(final SocketAddress endpoint, final int connectTimeout, final Proxy proxy, final SSLSocketFactory sslSocketFactory) {
super(maxRetries, name);
if(proxy == null && sslSocketFactory == null) { if(proxy == null && sslSocketFactory == null) {
openConnection = () -> { openConnection = () -> {
Socket s = null; Socket s = null;

View File

@ -0,0 +1 @@
com.moparisthebest.dns.listen.DefaultServices

View File

@ -0,0 +1 @@
com.moparisthebest.dns.resolve.DefaultServices