diff --git a/.dependabot/config.yml b/.dependabot/config.yml
new file mode 100644
index 0000000..12f6284
--- /dev/null
+++ b/.dependabot/config.yml
@@ -0,0 +1,25 @@
+version: 1
+update_configs:
+ - package_manager: "java:maven"
+ directory: "/"
+ update_schedule: "daily"
+ automerged_updates:
+ - match:
+ dependency_name: "org.apache.maven.plugins:*"
+ - match:
+ dependency_name: "org.codehaus.mojo:*"
+ - match:
+ dependency_name: "org.gaul:modernizer-maven-plugin"
+ - match:
+ dependency_name: "org.jacoco:jacoco-maven-plugin"
+ - match:
+ dependency_name: "org.checkerframework:checker-qual"
+ - match:
+ dependency_name: "org.assertj:assertj-core"
+ - match:
+ dependency_name: "org.openjdk.jmh:*"
+ - match:
+ dependency_name: "com.akathist.maven.plugins.launch4j:launch4j-maven-plugin"
+ - match:
+ dependency_name: "net.nicoulaj.maven.plugins:checksum-maven-plugin"
+
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 923a966..2780ff5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,26 +1,49 @@
+dist: xenial
language: java
jdk:
- - oraclejdk8
+ - openjdk8
+ - openjdk11
+ - openjdk-ea
-cache:
- directories:
- - $HOME/.m2
+matrix:
+ allow_failures:
+ - jdk: openjdk-ea
+
+env:
+ global:
+ - DEPLOY_JDK=openjdk11
+ - DEPLOY_REPO=nbbrd/java-net-proxy
deploy:
- # SNAPSHOTS from develop & jdk8
+ # Maven snapshots from develop branch
- provider: script
script: mvn deploy -Dmaven.test.skip -s .travis.settings.xml -P deploy-snapshots
skip_cleanup: true
on:
branch: develop
- jdk: oraclejdk8
- condition: $TRAVIS_PULL_REQUEST = "false"
-
- # RELEASES from master & jdk8
+ tags: false
+ repo: "${DEPLOY_REPO}"
+ condition: $TRAVIS_PULL_REQUEST == "false" && $TRAVIS_JDK_VERSION == $DEPLOY_JDK
+
+ # Maven releases from tags
- provider: script
script: mvn deploy -Dmaven.test.skip -s .travis.settings.xml -P deploy-releases
skip_cleanup: true
on:
- branch: master
- jdk: oraclejdk8
- condition: $TRAVIS_PULL_REQUEST = "false"
+ tags: true
+ repo: "${DEPLOY_REPO}"
+ condition: $TRAVIS_PULL_REQUEST == "false" && $TRAVIS_JDK_VERSION == $DEPLOY_JDK
+
+ # Github releases from tags
+ - provider: releases
+ api_key: "${GITHUB_KEY}"
+ skip_cleanup: true
+ draft: true
+ on:
+ tags: true
+ repo: "${DEPLOY_REPO}"
+ condition: $TRAVIS_PULL_REQUEST == "false" && $TRAVIS_JDK_VERSION == $DEPLOY_JDK
+
+cache:
+ directories:
+ - $HOME/.m2
diff --git a/pom.xml b/pom.xml
index 3da881a..f0cd268 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
be.nbb.rd
java-net-proxy
- 1.0.0-SNAPSHOT
+ 1.0.0
jar
java-net-proxy
@@ -21,44 +21,29 @@
https://joinup.ec.europa.eu/page/eupl-text-11-12
-
-
-
- UTF-8
- 1.8
- 1.8
- 2.4
- 0.7.9
-
-
- 1.16.18
- RELEASE82
- 4.12
- 3.11.1
- 3.0.2
-
-
- 1.1.0
-
org.projectlombok
lombok
- ${lombok.version}
provided
- org.netbeans.api
- org-openide-util-lookup
- ${netbeans.version}
+ be.nbb.rd
+ java-service-processor
provided
-
- com.google.code.findbugs
- jsr305
- ${jsr305.version}
+
+ org.checkerframework
+ checker-qual
+ 3.4.1
+ provided
+
+
+ com.github.stephenc.jcip
+ jcip-annotations
+ 1.0-1
provided
@@ -66,70 +51,281 @@
com.github.tuupertunut
powershell-lib-java
- ${powershell-lib-java.version}
+ 2.0.0
junit
junit
- ${junit.version}
+ 4.13
test
org.assertj
assertj-core
- ${assertj-core.version}
+ 3.16.1
test
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- ${maven-source-plugin.version}
-
-
- attach-sources
-
- jar
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
- ${jacoco-maven-plugin.version}
-
-
-
- prepare-agent
-
-
-
- report
- prepare-package
-
- report
-
-
-
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
-
-
-
-
+
+
+ base-java8
+
+
+ !skipBaseJava8
+
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+
+
+
+
+
+ base-processors
+
+
+ !skipBaseProcessors
+
+
+
+ 1.18.12
+ 1.3.0
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ be.nbb.rd
+ java-service-annotation
+ ${java-service.version}
+
+
+ be.nbb.rd
+ java-service-processor
+ ${java-service.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ be.nbb.rd
+ java-service-processor
+ ${java-service.version}
+
+
+
+
+
+
+
+
+
+
+ java8-with-jpms
+
+ [9,)
+
+
+
+ 9
+ 9
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ default-compile
+
+ 9
+
+
+
+
+ base-compile
+
+ compile
+
+
+ 8
+ 8
+
+ module-info.java
+
+
+
+
+
+
+
+
+
+
+
+ java8-without-jpms
+
+ 1.8
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ default-compile
+
+
+ module-info.java
+
+
+
+
+
+
+
+
+
+
+
+ enforce-dependency-rules
+
+
+ !skipEnforceDependencyRules
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 3.0.0-M3
+
+
+ enforce
+
+
+
+ 3.3.9
+
+
+
+ true
+
+
+
+
+ enforce
+
+
+
+
+
+
+
+
+
+
+ enforce-modern-api
+
+
+ !skipEnforceModernAPI
+
+
+
+
+
+ org.gaul
+ modernizer-maven-plugin
+ 2.1.0
+
+ 1.8
+
+
+
+ modernizer
+ verify
+
+ modernizer
+
+
+
+
+
+
+
+
+
+
+ enforce-code-coverage
+
+
+ !skipEnforceCodeCoverage
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.5
+
+
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+
+
+
+
+
+
deploy-snapshots
@@ -141,9 +337,23 @@
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 3.0.0-M1
+
org.apache.maven.plugins
maven-source-plugin
+ 3.2.1
+
+
+ verify
+
+ jar-no-fork
+
+
+
@@ -160,9 +370,23 @@
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 3.0.0-M1
+
org.apache.maven.plugins
maven-source-plugin
+ 3.2.1
+
+
+ verify
+
+ jar-no-fork
+
+
+
@@ -171,8 +395,11 @@
- netbeans
- http://bits.netbeans.org/maven2/
-
+ oss-jfrog-artifactory-releases
+ https://oss.jfrog.org/artifactory/oss-release-local
+
+ false
+
+
\ No newline at end of file
diff --git a/src/main/java/internal/net/proxy/FailsafeSystemProxySpi.java b/src/main/java/internal/net/proxy/FailsafeSystemProxySpi.java
new file mode 100644
index 0000000..84b0efa
--- /dev/null
+++ b/src/main/java/internal/net/proxy/FailsafeSystemProxySpi.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 National Bank of Belgium
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
+ * by the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * http://ec.europa.eu/idabc/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ */
+package internal.net.proxy;
+
+import java.net.Proxy;
+import java.net.URI;
+import java.util.Objects;
+import java.util.function.BiConsumer;
+import java.util.logging.Level;
+import nbbrd.net.proxy.SystemProxySelector;
+
+/**
+ *
+ * @author Philippe Charles
+ */
+@lombok.extern.java.Log
+@lombok.AllArgsConstructor
+public final class FailsafeSystemProxySpi implements SystemProxySelector.Spi {
+
+ public static SystemProxySelector.Spi wrap(SystemProxySelector.Spi delegate) {
+ return new FailsafeSystemProxySpi(delegate, FailsafeSystemProxySpi::logUnexpectedError);
+ }
+
+ @lombok.NonNull
+ private final SystemProxySelector.Spi delegate;
+
+ @lombok.NonNull
+ private final BiConsumer super String, ? super RuntimeException> onUnexpectedError;
+
+ @Override
+ public Proxy getProxyOrNull(URI uri) {
+ Objects.requireNonNull(uri);
+ try {
+ return delegate.getProxyOrNull(uri);
+ } catch (RuntimeException ex) {
+ onUnexpectedError.accept("While calling 'getProxyOrNull' on '" + delegate + "'", ex);
+ return null;
+ }
+ }
+
+ static void logUnexpectedError(String msg, RuntimeException ex) {
+ if (log.isLoggable(Level.WARNING)) {
+ log.log(Level.WARNING, msg, ex);
+ }
+ }
+}
diff --git a/src/main/java/com/github/tuupertunut/powershelllibjava/PowerShell.java b/src/main/java/internal/net/proxy/x/FixedPowerShell.java
similarity index 87%
rename from src/main/java/com/github/tuupertunut/powershelllibjava/PowerShell.java
rename to src/main/java/internal/net/proxy/x/FixedPowerShell.java
index b78a9f9..e6a53dc 100644
--- a/src/main/java/com/github/tuupertunut/powershelllibjava/PowerShell.java
+++ b/src/main/java/internal/net/proxy/x/FixedPowerShell.java
@@ -21,8 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package com.github.tuupertunut.powershelllibjava;
+package internal.net.proxy.x;
+import com.github.tuupertunut.powershelllibjava.AsyncReaderRecorder;
+import com.github.tuupertunut.powershelllibjava.PowerShellExecutionException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
@@ -43,7 +45,7 @@
*
* @author Tuupertunut
*/
-public class PowerShell implements Closeable {
+public class FixedPowerShell implements Closeable {
private static final String DEFAULT_WIN_EXECUTABLE = "powershell";
private static final String DEFAULT_CORE_EXECUTABLE = "pwsh";
@@ -64,7 +66,7 @@ public class PowerShell implements Closeable {
private boolean closed;
- private PowerShell(String psExecutable) throws IOException {
+ private FixedPowerShell(String psExecutable) throws IOException {
psSession = createProcessBuilder(psExecutable).start();
@@ -73,10 +75,10 @@ private PowerShell(String psExecutable) throws IOException {
outputRecorder = new AsyncReaderRecorder(commandOutput);
errorOutputRecorder = new AsyncReaderRecorder(commandErrorOutput);
- executor = Executors.newFixedThreadPool(2, r -> {
- Thread result = Executors.defaultThreadFactory().newThread(r);
- result.setDaemon(true);
- return result;
+ executor = Executors.newFixedThreadPool(2, (Runnable r) -> {
+ Thread thread = Executors.defaultThreadFactory().newThread(r);
+ thread.setDaemon(true);
+ return thread;
});
executor.execute(outputRecorder);
executor.execute(errorOutputRecorder);
@@ -107,7 +109,7 @@ private static ProcessBuilder createProcessBuilder(String psExecutable) {
*
* -Command - : Read commands from standard input stream of the
* process. */
- return new ProcessBuilder("cmd", "/c", "chcp", "65001", ">", "NUL", "&", psExecutable, "-ExecutionPolicy", "Bypass", "-NoExit", "-Command", "-");
+ return new ProcessBuilder("cmd", "/c", "chcp 65001 > NUL", "&", psExecutable, "-ExecutionPolicy", "Bypass", "-NoExit", "-Command", "-");
} else {
return new ProcessBuilder(psExecutable, "-ExecutionPolicy", "Bypass", "-NoExit", "-Command", "-");
}
@@ -135,8 +137,8 @@ private static String getDefaultExecutable() {
* @return a new PowerShell session.
* @throws IOException if an IOException occurred on process creation.
*/
- public static PowerShell open() throws IOException {
- return new PowerShell(getDefaultExecutable());
+ public static FixedPowerShell open() throws IOException {
+ return new FixedPowerShell(getDefaultExecutable());
}
/**
@@ -147,8 +149,8 @@ public static PowerShell open() throws IOException {
* @return a new PowerShell session.
* @throws IOException if an IOException occurred on process creation.
*/
- public static PowerShell open(String customExecutable) throws IOException {
- return new PowerShell(customExecutable);
+ public static FixedPowerShell open(String customExecutable) throws IOException {
+ return new FixedPowerShell(customExecutable);
}
/**
@@ -159,6 +161,10 @@ public static PowerShell open(String customExecutable) throws IOException {
public void close() {
closed = true;
if (commandInput != null) {
+
+ /* Sending a shutdown signal to PowerShell. */
+ commandInput.println("exit");
+
commandInput.close();
}
if (executor != null) {
@@ -176,9 +182,6 @@ public void close() {
} catch (IOException ex) {
}
}
- if (psSession != null) {
- psSession.destroy();
- }
}
/**
@@ -205,13 +208,16 @@ public void close() {
* @throws IOException if an IOException occurred while reading the output
* of the commands.
* @throws IllegalStateException if this PowerShell session was already
- * closed.
- * @throws RuntimeException if the output stream ended too early, or if the
- * current thread was interrupted while executing.
+ * closed, or the process or its output stream has terminated too early.
+ * @throws RuntimeException if the current thread was interrupted while
+ * executing.
*/
public String executeCommands(String... commands) throws PowerShellExecutionException, IOException {
if (closed) {
throw new IllegalStateException("This PowerShell session has been closed.");
+ } else if (!psSession.isAlive()) {
+ close();
+ throw new IllegalStateException("The PowerShell process has terminated before it should.");
}
StringBuilder commandChainBuilder = new StringBuilder();
@@ -243,7 +249,8 @@ public String executeCommands(String... commands) throws PowerShellExecutionExce
* circumstances. */
Optional optionalOutput = outputRecorder.consumeToNextDelimiter(END_OF_COMMAND + System.lineSeparator());
if (!optionalOutput.isPresent()) {
- throw new RuntimeException("PowerShell output stream ended too early.");
+ close();
+ throw new IllegalStateException("PowerShell output stream ended too early.");
}
String output = optionalOutput.get().replace(END_OF_COMMAND + System.lineSeparator(), "");
diff --git a/src/main/java/internal/net/SinglePowerShell.java b/src/main/java/internal/net/proxy/x/SharedPowerShell.java
similarity index 91%
rename from src/main/java/internal/net/SinglePowerShell.java
rename to src/main/java/internal/net/proxy/x/SharedPowerShell.java
index 27b7aeb..7112c4a 100644
--- a/src/main/java/internal/net/SinglePowerShell.java
+++ b/src/main/java/internal/net/proxy/x/SharedPowerShell.java
@@ -14,29 +14,30 @@
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
-package internal.net;
+package internal.net.proxy.x;
-import com.github.tuupertunut.powershelllibjava.PowerShell;
import com.github.tuupertunut.powershelllibjava.PowerShellExecutionException;
import java.io.IOException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
+import net.jcip.annotations.ThreadSafe;
/**
*
* @author Philippe Charles
*/
-public final class SinglePowerShell {
+@ThreadSafe
+public final class SharedPowerShell {
private static final long MAIN_TIMEOUT_MILLIS = 1000 * 10;
private static final int FALLBACK_MAX_INSTANCES = 3;
private final ReentrantLock lock;
private final Semaphore fallbackInstances;
- private PowerShell ps;
+ private FixedPowerShell ps;
- public SinglePowerShell() {
+ public SharedPowerShell() {
this.lock = new ReentrantLock();
this.fallbackInstances = new Semaphore(FALLBACK_MAX_INSTANCES);
this.ps = null;
@@ -62,7 +63,7 @@ public String executeCommands(String cmd) throws IOException, PowerShellExecutio
private String execOnMain(String cmd) throws IOException, PowerShellExecutionException {
try {
if (ps == null) {
- ps = PowerShell.open();
+ ps = FixedPowerShell.open();
} else {
}
return ps.executeCommands(cmd);
@@ -74,7 +75,7 @@ private String execOnMain(String cmd) throws IOException, PowerShellExecutionExc
private String execOnFallback(String cmd) throws IOException, PowerShellExecutionException {
if (fallbackInstances.tryAcquire()) {
- try (PowerShell temp = PowerShell.open()) {
+ try (FixedPowerShell temp = FixedPowerShell.open()) {
return temp.executeCommands(cmd);
} finally {
fallbackInstances.release();
diff --git a/src/main/java/internal/net/TtlCache.java b/src/main/java/internal/net/proxy/x/TtlCache.java
similarity index 84%
rename from src/main/java/internal/net/TtlCache.java
rename to src/main/java/internal/net/proxy/x/TtlCache.java
index 1b9f651..6cad488 100644
--- a/src/main/java/internal/net/TtlCache.java
+++ b/src/main/java/internal/net/proxy/x/TtlCache.java
@@ -14,7 +14,7 @@
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
-package internal.net;
+package internal.net.proxy.x;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -22,8 +22,8 @@
import java.util.function.Function;
import java.util.function.LongSupplier;
import java.util.logging.Level;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
*
@@ -35,13 +35,13 @@
@lombok.Builder(builderClassName = "Builder", toBuilder = true)
public final class TtlCache {
- @Nonnull
+ @NonNull
public static TtlCache of() {
return builder()
.minTtlInMillis(10)
.maxTtlInMillis(1000 * 60)
.ttlFactor(10)
- .cache(new ConcurrentHashMap<>())
+ .storage(new ConcurrentHashMap<>())
.clock(System::currentTimeMillis)
.onEvent(TtlCache::logEvent)
.build();
@@ -50,15 +50,15 @@ public static TtlCache of() {
private final long minTtlInMillis;
private final long maxTtlInMillis;
private final long ttlFactor;
- private final ConcurrentMap> cache;
+ private final ConcurrentMap> storage;
private final LongSupplier clock;
private final BiConsumer super K, Event> onEvent;
private final int evictThreshold = 1000;
@Nullable
- public V get(@Nonnull K key, @Nonnull Function loader) {
+ public V get(@NonNull K key, @NonNull Function loader) {
long now = clock.getAsLong();
- Entry entry = cache.get(key);
+ Entry entry = storage.get(key);
if (entry != null) {
if (!entry.hasExpired(now)) {
onEvent.accept(key, Event.HIT);
@@ -77,22 +77,22 @@ private V load(K key, Function loader, long before, boolean miss) {
if (ttl >= minTtlInMillis) {
onEvent.accept(key, miss ? Event.MISS_SLOW : Event.EXP_SLOW);
evictExpiredEntries(after);
- cache.put(key, new Entry<>(after + Math.min(maxTtlInMillis, ttl), result));
+ storage.put(key, new Entry<>(after + Math.min(maxTtlInMillis, ttl), result));
} else {
onEvent.accept(key, miss ? Event.MISS_FAST : Event.EXP_FAST);
if (!miss) {
- cache.remove(key);
+ storage.remove(key);
}
}
return result;
}
private void evictExpiredEntries(long currentTimeInMillis) {
- if (cache.size() > evictThreshold) {
- cache.entrySet()
+ if (storage.size() > evictThreshold) {
+ storage.entrySet()
.stream()
.filter(o -> o.getValue().hasExpired(currentTimeInMillis))
- .forEach(cache::remove);
+ .forEach(storage::remove);
}
}
diff --git a/src/main/java/internal/net/WinPowerShellProxySelector.java b/src/main/java/internal/net/proxy/x/WinPowerShellProxySelector.java
similarity index 95%
rename from src/main/java/internal/net/WinPowerShellProxySelector.java
rename to src/main/java/internal/net/proxy/x/WinPowerShellProxySelector.java
index a3c5998..9571bf2 100644
--- a/src/main/java/internal/net/WinPowerShellProxySelector.java
+++ b/src/main/java/internal/net/proxy/x/WinPowerShellProxySelector.java
@@ -14,7 +14,7 @@
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
-package internal.net;
+package internal.net.proxy.x;
import com.github.tuupertunut.powershelllibjava.PowerShellExecutionException;
import java.io.IOException;
@@ -28,14 +28,14 @@
import java.util.function.UnaryOperator;
import java.util.logging.Level;
import lombok.AccessLevel;
-import nbbrd.net.SystemProxySelector;
-import org.openide.util.lookup.ServiceProvider;
+import nbbrd.net.proxy.SystemProxySelector;
+import nbbrd.service.ServiceProvider;
/**
*
* @author Philippe Charles
*/
-@ServiceProvider(service = SystemProxySelector.Spi.class)
+@ServiceProvider(SystemProxySelector.Spi.class)
@lombok.extern.java.Log
@lombok.AllArgsConstructor(access = AccessLevel.PACKAGE)
public final class WinPowerShellProxySelector implements SystemProxySelector.Spi {
@@ -112,7 +112,7 @@ private static void logCacheEvent(Object key, TtlCache.Event e) {
public static final class GetSystemWebProxyCommand implements Function {
- private final SinglePowerShell ps = new SinglePowerShell();
+ private final SharedPowerShell ps = new SharedPowerShell();
@Override
public String apply(URI uri) {
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..d07d106
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 National Bank of Belgium
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
+ * by the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * http://ec.europa.eu/idabc/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ */
+
+module nbbrd.net.proxy {
+ requires static lombok;
+ requires static nbbrd.service;
+ requires static org.checkerframework.checker.qual;
+ requires static jcip.annotations;
+
+ requires java.logging;
+ requires com.github.tuupertunut.powershelllibjava;
+
+ exports nbbrd.net.proxy;
+
+ provides nbbrd.net.proxy.SystemProxySelector.Spi with
+ internal.net.proxy.x.WinPowerShellProxySelector;
+
+ uses nbbrd.net.proxy.SystemProxySelector.Spi;
+}
diff --git a/src/main/java/nbbrd/net/SystemProxySelector.java b/src/main/java/nbbrd/net/proxy/SystemProxySelector.java
similarity index 66%
rename from src/main/java/nbbrd/net/SystemProxySelector.java
rename to src/main/java/nbbrd/net/proxy/SystemProxySelector.java
index f099a3d..cfdc328 100644
--- a/src/main/java/nbbrd/net/SystemProxySelector.java
+++ b/src/main/java/nbbrd/net/proxy/SystemProxySelector.java
@@ -14,8 +14,10 @@
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
-package nbbrd.net;
+package nbbrd.net.proxy;
+import internal.net.proxy.FailsafeSystemProxySpi;
+import internal.net.proxy.SystemProxySpiLoader;
import java.io.IOException;
import java.net.Proxy;
import java.net.ProxySelector;
@@ -24,35 +26,27 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.ServiceLoader;
-import java.util.function.BiConsumer;
import java.util.function.UnaryOperator;
-import java.util.logging.Level;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.ThreadSafe;
+import nbbrd.service.Quantifier;
+import nbbrd.service.ServiceDefinition;
+import net.jcip.annotations.ThreadSafe;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
*
* @author Philippe Charles
*/
@ThreadSafe
-@lombok.extern.java.Log
@lombok.Builder(builderClassName = "Builder", toBuilder = true)
public final class SystemProxySelector extends ProxySelector {
- @Nonnull
+ @NonNull
public static SystemProxySelector ofServiceLoader() {
- List providers = StreamSupport
- .stream(ServiceLoader.load(Spi.class).spliterator(), false)
- .collect(Collectors.toList());
return builder()
- .providers(providers)
+ .providers(new SystemProxySpiLoader().get())
.systemProperties(System::getProperty)
.fallback(ProxySelector.getDefault())
- .onUnexpectedError(SystemProxySelector::logUnexpectedError)
.build();
}
@@ -65,9 +59,6 @@ public static SystemProxySelector ofServiceLoader() {
@lombok.NonNull
private final ProxySelector fallback;
- @lombok.NonNull
- private final BiConsumer super String, ? super RuntimeException> onUnexpectedError;
-
@Override
public List select(URI uri) {
if (uri == null) {
@@ -77,7 +68,7 @@ public List select(URI uri) {
return fallback.select(uri);
}
return providers.stream()
- .map(o -> tryGetProxyOrNull(o, uri))
+ .map(provider -> provider.getProxyOrNull(uri))
.filter(Objects::nonNull)
.findFirst()
.map(Collections::singletonList)
@@ -89,15 +80,6 @@ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
fallback.connectFailed(uri, sa, ioe);
}
- private Proxy tryGetProxyOrNull(Spi o, URI uri) {
- try {
- return o.getProxyOrNull(uri);
- } catch (RuntimeException ex) {
- onUnexpectedError.accept("While calling 'getProxyOrNull' on '" + o + "'", ex);
- return null;
- }
- }
-
private boolean hasStaticProxyProperties() {
return hasProperty("https.proxyPort")
|| hasProperty("https.proxyHost")
@@ -110,16 +92,14 @@ private boolean hasProperty(String property) {
return systemProperties.apply(property) != null;
}
- private static void logUnexpectedError(String msg, RuntimeException ex) {
- if (log.isLoggable(Level.WARNING)) {
- log.log(Level.WARNING, msg, ex);
- }
- }
-
@ThreadSafe
+ @ServiceDefinition(
+ quantifier = Quantifier.MULTIPLE,
+ wrapper = FailsafeSystemProxySpi.class,
+ loaderName = "internal.net.proxy.SystemProxySpiLoader")
public interface Spi {
@Nullable
- Proxy getProxyOrNull(@Nonnull URI uri);
+ Proxy getProxyOrNull(@NonNull URI uri);
}
}
diff --git a/src/test/java/_demo/PowerShellDemo.java b/src/test/java/_demo/SharedPowerShellDemo.java
similarity index 93%
rename from src/test/java/_demo/PowerShellDemo.java
rename to src/test/java/_demo/SharedPowerShellDemo.java
index 177779a..8143555 100644
--- a/src/test/java/_demo/PowerShellDemo.java
+++ b/src/test/java/_demo/SharedPowerShellDemo.java
@@ -16,7 +16,7 @@
*/
package _demo;
-import internal.net.SinglePowerShell;
+import internal.net.proxy.x.SharedPowerShell;
import java.util.List;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
@@ -26,7 +26,7 @@
*
* @author Philippe Charles
*/
-public class PowerShellDemo {
+public class SharedPowerShellDemo {
public static void main(String[] args) throws Exception {
List cmds = IntStream.range(0, 10).mapToObj(i -> "echo " + i).collect(Collectors.toList());
@@ -38,7 +38,7 @@ public static void main(String[] args) throws Exception {
}
private static ToLongFunction newTimer() {
- SinglePowerShell ps = new SinglePowerShell();
+ SharedPowerShell ps = new SharedPowerShell();
// try {
// ps.executeCommands("echo 'warming'");
// } catch (IOException | PowerShellExecutionException ex) {
diff --git a/src/test/java/_demo/SystemProxySelectorDemo.java b/src/test/java/_demo/SystemProxySelectorDemo.java
new file mode 100644
index 0000000..4d772d4
--- /dev/null
+++ b/src/test/java/_demo/SystemProxySelectorDemo.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 National Bank of Belgium
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
+ * by the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * http://ec.europa.eu/idabc/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ */
+package _demo;
+
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+import java.util.stream.Stream;
+import nbbrd.net.proxy.SystemProxySelector;
+
+/**
+ *
+ * @author Philippe Charles
+ */
+public class SystemProxySelectorDemo {
+
+ public static void main(String[] args) throws URISyntaxException {
+ URI[] x = {
+ new URI("http://ec.europa.eu"),
+ new URI("http://dataservices.imf.org"),
+ new URI("http://data.un.org"),
+ new URI("https://sdmxcentral.imf.org"),
+ new URI("http://andmebaas.stat.ee"),
+ new URI("http://www.ilo.org"),
+ new URI("http://bdm.insee.fr"),
+ new URI("https://sdw-wsrest.ecb.europa.eu"),
+ new URI("http://sdmx.snieg.mx"),
+ new URI("https://stats.oecd.org"),
+ new URI("http://stat.data.abs.gov.au"),
+ new URI("https://stat.nbb.be"),
+ new URI("http://wits.worldbank.org"),
+ new URI("http://data.uis.unesco.org"),
+ new URI("https://api.worldbank.org"),
+ new URI("http://sdmx.istat.it")
+ };
+
+ SystemProxySelector proxySelector = SystemProxySelector.ofServiceLoader();
+ Stream.of(x).parallel().forEach(uri -> fetchAndPrint(uri, proxySelector));
+ }
+
+ private static void fetchAndPrint(URI uri, ProxySelector proxySelector) {
+ long start = System.currentTimeMillis();
+ List proxies = proxySelector.select(uri);
+ long stop = System.currentTimeMillis();
+ System.out.println(uri + " >>> " + proxies + " >>> " + (stop - start));
+ }
+}
diff --git a/src/test/java/_demo/WinMapDemo.java b/src/test/java/_demo/WinMapDemo.java
index cdc1e36..01a7793 100644
--- a/src/test/java/_demo/WinMapDemo.java
+++ b/src/test/java/_demo/WinMapDemo.java
@@ -16,7 +16,7 @@
*/
package _demo;
-import internal.net.WinPowerShellProxySelector;
+import internal.net.proxy.x.WinPowerShellProxySelector;
import java.net.URI;
import java.net.URISyntaxException;
diff --git a/src/test/java/_demo/WinParallelDemo.java b/src/test/java/_demo/WinParallelDemo.java
index f09e115..f1aa583 100644
--- a/src/test/java/_demo/WinParallelDemo.java
+++ b/src/test/java/_demo/WinParallelDemo.java
@@ -16,7 +16,7 @@
*/
package _demo;
-import internal.net.WinPowerShellProxySelector;
+import internal.net.proxy.x.WinPowerShellProxySelector;
import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
diff --git a/src/test/java/_test/ProxyMap.java b/src/test/java/_test/ProxyMap.java
new file mode 100644
index 0000000..a7ffdea
--- /dev/null
+++ b/src/test/java/_test/ProxyMap.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2019 National Bank of Belgium
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
+ * by the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * http://ec.europa.eu/idabc/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ */
+package _test;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author Philippe Charles
+ */
+@lombok.Builder
+public final class ProxyMap extends ProxySelector {
+
+ @lombok.Singular
+ private final Map proxies;
+
+ @Override
+ public List select(URI uri) {
+ Proxy result = proxies.get(uri);
+ return result != null ? Collections.singletonList(result) : Collections.emptyList();
+ }
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ }
+}
diff --git a/src/test/java/internal/net/proxy/FailsafeSystemProxySpiTest.java b/src/test/java/internal/net/proxy/FailsafeSystemProxySpiTest.java
new file mode 100644
index 0000000..9171aec
--- /dev/null
+++ b/src/test/java/internal/net/proxy/FailsafeSystemProxySpiTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 National Bank of Belgium
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
+ * by the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * http://ec.europa.eu/idabc/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ */
+package internal.net.proxy;
+
+import _test.LogCollector;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.function.BiConsumer;
+import static org.assertj.core.api.Assertions.*;
+import org.junit.Test;
+
+/**
+ *
+ * @author Philippe Charles
+ */
+public class FailsafeSystemProxySpiTest {
+
+ @Test
+ public void testFactories() throws URISyntaxException {
+ assertThatNullPointerException()
+ .isThrownBy(() -> new FailsafeSystemProxySpi(null, this::doNothing));
+
+ assertThatNullPointerException()
+ .isThrownBy(() -> new FailsafeSystemProxySpi(this::pass, null));
+
+ assertThatNullPointerException()
+ .isThrownBy(() -> FailsafeSystemProxySpi.wrap(null));
+ }
+
+ @Test
+ public void testGetProxyOrNull() throws URISyntaxException {
+ URI uri = new URI("https://www.nbb.be");
+
+ Queue errorStack = new LinkedList<>();
+ BiConsumer pushError = (m, e) -> errorStack.add(e);
+
+ assertThatNullPointerException()
+ .isThrownBy(() -> new FailsafeSystemProxySpi(this::pass, pushError).getProxyOrNull(null));
+ assertThat(errorStack).isEmpty();
+
+ assertThat(new FailsafeSystemProxySpi(this::pass, pushError).getProxyOrNull(uri)).isEqualTo(mainProxy);
+ assertThat(errorStack).isEmpty();
+
+ assertThat(new FailsafeSystemProxySpi(this::fail, pushError).getProxyOrNull(uri)).isNull();
+ assertThat(errorStack).hasSize(1);
+ }
+
+ @Test
+ public void testLogUnexpectedError() throws URISyntaxException {
+ URI uri = new URI("https://www.nbb.be");
+
+ try (LogCollector logs = LogCollector.of(FailsafeSystemProxySpi.class)) {
+ new FailsafeSystemProxySpi(this::fail, FailsafeSystemProxySpi::logUnexpectedError).getProxyOrNull(uri);
+ assertThat(logs)
+ .hasSize(1)
+ .element(0)
+ .extracting(record -> record.getThrown().getMessage())
+ .asString()
+ .contains("boom");
+ }
+ }
+
+ private final Proxy mainProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("main", 1234));
+
+ private Proxy pass(URI o) {
+ return mainProxy;
+ }
+
+ private Proxy fail(URI o) {
+ throw new RuntimeException("boom");
+ }
+
+ private void doNothing(String msg, Exception ex) {
+ }
+}
diff --git a/src/test/java/internal/net/TtlCacheTest.java b/src/test/java/internal/net/proxy/x/TtlCacheTest.java
similarity index 85%
rename from src/test/java/internal/net/TtlCacheTest.java
rename to src/test/java/internal/net/proxy/x/TtlCacheTest.java
index 811599d..f216284 100644
--- a/src/test/java/internal/net/TtlCacheTest.java
+++ b/src/test/java/internal/net/proxy/x/TtlCacheTest.java
@@ -14,7 +14,7 @@
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*/
-package internal.net;
+package internal.net.proxy.x;
import _test.LogCollector;
import java.util.concurrent.ConcurrentHashMap;
@@ -35,7 +35,7 @@ public class TtlCacheTest {
@Test
@SuppressWarnings("null")
public void test() {
- ConcurrentMap> map = new ConcurrentHashMap<>();
+ ConcurrentMap> storage = new ConcurrentHashMap<>();
AtomicLong clock = new AtomicLong(0);
AtomicInteger counter = new AtomicInteger();
AtomicReference event = new AtomicReference<>();
@@ -46,7 +46,7 @@ public void test() {
.minTtlInMillis(10)
.maxTtlInMillis(1000)
.ttlFactor(1)
- .cache(map)
+ .storage(storage)
.clock(clock::get)
.onEvent((k, e) -> event.set(e))
.build();
@@ -62,40 +62,44 @@ public void test() {
duration.set(5);
assertThat(cache.get("a", loader)).isEqualTo(1);
assertThat(event).hasValue(TtlCache.Event.MISS_FAST);
- assertThat(map).doesNotContainKey("a");
+ assertThat(storage).doesNotContainKey("a");
assertThat(clock).hasValue(5);
duration.set(10);
assertThat(cache.get("a", loader)).isEqualTo(2);
assertThat(event).hasValue(TtlCache.Event.MISS_SLOW);
- assertThat(map).containsKey("a");
+ assertThat(storage).containsKey("a");
assertThat(clock).hasValue(15);
duration.set(10);
assertThat(cache.get("a", loader)).isEqualTo(2);
assertThat(event).hasValue(TtlCache.Event.HIT);
- assertThat(map).containsKey("a");
+ assertThat(storage).containsKey("a");
assertThat(clock).hasValue(15);
clock.addAndGet(1000);
duration.set(10);
assertThat(cache.get("a", loader)).isEqualTo(3);
assertThat(event).hasValue(TtlCache.Event.EXP_SLOW);
- assertThat(map).containsKey("a");
+ assertThat(storage).containsKey("a");
assertThat(clock).hasValue(1025);
clock.addAndGet(1000);
duration.set(5);
assertThat(cache.get("a", loader)).isEqualTo(4);
assertThat(event).hasValue(TtlCache.Event.EXP_FAST);
- assertThat(map).doesNotContainKey("a");
+ assertThat(storage).doesNotContainKey("a");
assertThat(clock).hasValue(2030);
}
@Test
public void testLog() {
try (LogCollector logs = LogCollector.of(TtlCache.class)) {
- TtlCache.of().get("hello", o -> "world");
+ TtlCache.of()
+ .toBuilder()
+ .clock(() -> 0)
+ .build()
+ .get("hello", o -> "world");
assertThat(logs)
.hasSize(1)
.element(0)
diff --git a/src/test/java/internal/net/proxy/x/WinPowerShellProxySelectorTest.java b/src/test/java/internal/net/proxy/x/WinPowerShellProxySelectorTest.java
new file mode 100644
index 0000000..f09fb40
--- /dev/null
+++ b/src/test/java/internal/net/proxy/x/WinPowerShellProxySelectorTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 National Bank of Belgium
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
+ * by the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * http://ec.europa.eu/idabc/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ */
+package internal.net.proxy.x;
+
+import java.util.ServiceLoader;
+import nbbrd.net.proxy.SystemProxySelector;
+import static org.assertj.core.api.Assertions.assertThat;
+import org.junit.Test;
+
+/**
+ *
+ * @author Philippe Charles
+ */
+public class WinPowerShellProxySelectorTest {
+
+ @Test
+ public void testRegistration() {
+ assertThat(ServiceLoader.load(SystemProxySelector.Spi.class))
+ .anyMatch(WinPowerShellProxySelector.class::isInstance);
+ }
+}
diff --git a/src/test/java/nbbrd/net/SystemProxySelectorTest.java b/src/test/java/nbbrd/net/SystemProxySelectorTest.java
deleted file mode 100644
index 64dbe92..0000000
--- a/src/test/java/nbbrd/net/SystemProxySelectorTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright 2018 National Bank of Belgium
- *
- * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
- * by the European Commission - subsequent versions of the EUPL (the "Licence");
- * You may not use this work except in compliance with the Licence.
- * You may obtain a copy of the Licence at:
- *
- * http://ec.europa.eu/idabc/eupl
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the Licence is distributed on an "AS IS" basis,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing permissions and
- * limitations under the Licence.
- */
-package nbbrd.net;
-
-import _test.LogCollector;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.ProxySelector;
-import java.net.SocketAddress;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.function.BiConsumer;
-import java.util.function.UnaryOperator;
-import static org.assertj.core.api.Assertions.*;
-import org.junit.Test;
-
-/**
- *
- * @author Philippe Charles
- */
-public class SystemProxySelectorTest {
-
- @Test
- public void test() throws URISyntaxException {
- URI uri = new URI("https://www.nbb.be");
- Queue errorStack = new LinkedList<>();
- ProxySelector fallbackSelector = new FallbackProxySelector(fallbackProxy);
- UnaryOperator noProperties = o -> null;
- BiConsumer pushError = (m, e) -> errorStack.add(e);
-
- assertThatIllegalArgumentException()
- .isThrownBy(
- () -> SystemProxySelector
- .builder()
- .systemProperties(noProperties)
- .fallback(fallbackSelector)
- .onUnexpectedError(pushError)
- .build()
- .select(null)
- );
-
- assertThat(SystemProxySelector
- .builder()
- .systemProperties(noProperties)
- .fallback(fallbackSelector)
- .onUnexpectedError(pushError)
- .build()
- .select(uri)
- ).containsExactly(fallbackProxy);
- assertThat(errorStack).isEmpty();
-
- assertThat(SystemProxySelector
- .builder()
- .provider(o -> mainProxy)
- .systemProperties(noProperties)
- .fallback(fallbackSelector)
- .onUnexpectedError(pushError)
- .build()
- .select(uri)
- ).containsExactly(mainProxy);
- assertThat(errorStack).isEmpty();
-
- assertThat(SystemProxySelector
- .builder()
- .provider(o -> mainProxy)
- .systemProperties(o -> "http.proxyHost")
- .fallback(fallbackSelector)
- .onUnexpectedError(pushError)
- .build()
- .select(uri)
- ).containsExactly(fallbackProxy);
- assertThat(errorStack).isEmpty();
-
- assertThat(SystemProxySelector
- .builder()
- .provider(this::boom)
- .systemProperties(noProperties)
- .fallback(fallbackSelector)
- .onUnexpectedError(pushError)
- .build()
- .select(uri)
- ).containsExactly(fallbackProxy);
- assertThat(errorStack).hasSize(1);
- }
-
- @Test
- public void testLog() throws URISyntaxException {
- try (LogCollector logs = LogCollector.of(SystemProxySelector.class)) {
- SystemProxySelector.ofServiceLoader()
- .toBuilder()
- .clearProviders()
- .provider(this::boom)
- .build()
- .select(new URI("https://www.nbb.be"));
- assertThat(logs)
- .hasSize(1)
- .element(0)
- .extracting(o -> o.getThrown().getMessage())
- .asString()
- .contains("boom");
- }
- }
-
- private final Proxy mainProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("main", 1234));
- private final Proxy fallbackProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("fallback", 1234));
-
- private Proxy boom(URI o) {
- throw new RuntimeException("boom");
- }
-
- @lombok.AllArgsConstructor
- private static final class FallbackProxySelector extends ProxySelector {
-
- private final Proxy proxy;
-
- @Override
- public List select(URI uri) {
- return Collections.singletonList(proxy);
- }
-
- @Override
- public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
- }
- }
-}
diff --git a/src/test/java/nbbrd/net/proxy/SystemProxySelectorTest.java b/src/test/java/nbbrd/net/proxy/SystemProxySelectorTest.java
new file mode 100644
index 0000000..fb8d57d
--- /dev/null
+++ b/src/test/java/nbbrd/net/proxy/SystemProxySelectorTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018 National Bank of Belgium
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
+ * by the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ *
+ * http://ec.europa.eu/idabc/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ */
+package nbbrd.net.proxy;
+
+import _test.ProxyMap;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.function.UnaryOperator;
+import static org.assertj.core.api.Assertions.*;
+import org.junit.Test;
+
+/**
+ *
+ * @author Philippe Charles
+ */
+public class SystemProxySelectorTest {
+
+ @Test
+ public void test() throws URISyntaxException {
+ URI uri = new URI("https://www.nbb.be");
+
+ ProxySelector fallbackSelector = ProxyMap.builder().proxy(uri, fallbackProxy).build();
+ UnaryOperator noProperties = o -> null;
+
+ assertThatIllegalArgumentException()
+ .isThrownBy(
+ () -> SystemProxySelector
+ .builder()
+ .systemProperties(noProperties)
+ .fallback(fallbackSelector)
+ .build()
+ .select(null)
+ );
+
+ assertThat(SystemProxySelector
+ .builder()
+ .systemProperties(noProperties)
+ .fallback(fallbackSelector)
+ .build()
+ .select(uri)
+ ).containsExactly(fallbackProxy);
+
+ assertThat(SystemProxySelector
+ .builder()
+ .provider(o -> mainProxy)
+ .systemProperties(noProperties)
+ .fallback(fallbackSelector)
+ .build()
+ .select(uri)
+ ).containsExactly(mainProxy);
+
+ assertThat(SystemProxySelector
+ .builder()
+ .provider(o -> mainProxy)
+ .systemProperties(o -> "http.proxyHost")
+ .fallback(fallbackSelector)
+ .build()
+ .select(uri)
+ ).containsExactly(fallbackProxy);
+ }
+
+ private final Proxy mainProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("main", 1234));
+ private final Proxy fallbackProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("fallback", 1234));
+}