From ec0d1278d374c165f6cc1001cbd2e46a2b739148 Mon Sep 17 00:00:00 2001 From: "Jonathan K. Henderson" Date: Sun, 1 Oct 2023 15:48:10 -0400 Subject: [PATCH] separated mutable and immutable from SelectionStrategy --- .../jargyle/cli/ServerConfigurationCLI.java | 4 +- .../server/GeneralSettingSpecConstants.java | 2 +- .../jargyle/server/SelectionStrategy.java | 169 ++++-------------- .../jargyle/server/SelectionStrategySpec.java | 61 +++++++ .../SelectionStrategySpecConstants.java | 74 ++++++++ .../server/SelectionStrategySpecs.java | 34 ++++ .../impl/SelectionStrategyRuleResultSpec.java | 3 +- .../impl/CyclicalSelectionStrategy.java | 35 ++++ .../impl/RandomSelectionStrategy.java | 31 ++++ .../impl/SelectionStrategySettingSpec.java | 3 +- 10 files changed, 275 insertions(+), 141 deletions(-) create mode 100644 jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpec.java create mode 100644 jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpecConstants.java create mode 100644 jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpecs.java create mode 100644 jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/selectionstrategy/impl/CyclicalSelectionStrategy.java create mode 100644 jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/selectionstrategy/impl/RandomSelectionStrategy.java diff --git a/jargyle-cli/src/main/java/com/github/jh3nd3rs0n/jargyle/cli/ServerConfigurationCLI.java b/jargyle-cli/src/main/java/com/github/jh3nd3rs0n/jargyle/cli/ServerConfigurationCLI.java index d1cb70d01..7fae054d1 100644 --- a/jargyle-cli/src/main/java/com/github/jh3nd3rs0n/jargyle/cli/ServerConfigurationCLI.java +++ b/jargyle-cli/src/main/java/com/github/jh3nd3rs0n/jargyle/cli/ServerConfigurationCLI.java @@ -29,7 +29,7 @@ import com.github.jh3nd3rs0n.jargyle.server.GeneralRuleResultSpecConstants; import com.github.jh3nd3rs0n.jargyle.server.GeneralSettingSpecConstants; import com.github.jh3nd3rs0n.jargyle.server.LogAction; -import com.github.jh3nd3rs0n.jargyle.server.SelectionStrategy; +import com.github.jh3nd3rs0n.jargyle.server.SelectionStrategySpecConstants; import com.github.jh3nd3rs0n.jargyle.server.Setting; import com.github.jh3nd3rs0n.jargyle.server.Socks5RuleConditionSpecConstants; import com.github.jh3nd3rs0n.jargyle.server.Socks5RuleResultSpecConstants; @@ -457,7 +457,7 @@ private void printSettingValueSyntaxes() { System.out.println(" SCHEMES:"); this.printHelpText(Scheme.class); System.out.println(" SELECTION_STRATEGIES:"); - this.printHelpText(SelectionStrategy.class); + this.printHelpText(SelectionStrategySpecConstants.class); System.out.println(" SOCKET_SETTINGS:"); this.printHelpText(StandardSocketSettingSpecConstants.class); System.out.println(" SOCKS5_COMMANDS:"); diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/GeneralSettingSpecConstants.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/GeneralSettingSpecConstants.java index 585dc3087..42fc2eaa9 100644 --- a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/GeneralSettingSpecConstants.java +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/GeneralSettingSpecConstants.java @@ -189,7 +189,7 @@ public final class GeneralSettingSpecConstants { public static final SettingSpec ROUTE_SELECTION_STRATEGY = SETTING_SPECS.addThenGet(new SelectionStrategySettingSpec( "routeSelectionStrategy", - SelectionStrategy.CYCLICAL.newMutableInstance())); + SelectionStrategySpecConstants.CYCLICAL.newSelectionStrategy())); @HelpText( doc = "A rule for the SOCKS server " diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategy.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategy.java index d4c3e3442..7e7ae83dc 100644 --- a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategy.java +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategy.java @@ -1,129 +1,38 @@ package com.github.jh3nd3rs0n.jargyle.server; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; -import com.github.jh3nd3rs0n.jargyle.internal.annotation.HelpText; - public abstract class SelectionStrategy { - - private static final class CyclicalSelectionStrategy extends SelectionStrategy { - - private int index; - private final ReentrantLock lock; - - private CyclicalSelectionStrategy(final String str, final boolean mut) { - super(str, mut); - this.index = 0; - this.lock = new ReentrantLock(); - } - - @Override - protected T implSelectFrom(final List list) { - T selected = null; - this.lock.lock(); - try { - if (this.index > list.size() - 1) { - this.index = 0; - } - selected = list.get(this.index); - this.index++; - } finally { - this.lock.unlock(); - } - return selected; - } - - @Override - public SelectionStrategy newMutableInstance() { - return new CyclicalSelectionStrategy(this.toString(), true); - } - - } - - private static final class RandomSelectionStrategy extends SelectionStrategy { - - private final ReentrantLock lock; - private final Random random; - - private RandomSelectionStrategy(final String str, final boolean mut) { - super(str, mut); - this.lock = new ReentrantLock(); - this.random = (mut) ? new Random() : null; - } - - @Override - protected T implSelectFrom(final List list) { - T selected = null; - this.lock.lock(); - try { - selected = list.get(this.random.nextInt(list.size())); - } finally { - this.lock.unlock(); - } - return selected; - } - - @Override - public SelectionStrategy newMutableInstance() { - return new RandomSelectionStrategy(this.toString(), true); - } - - } - - private static final List VALUES = new ArrayList(); - private static final Map VALUES_MAP = new HashMap(); - @HelpText( - doc = "Select the next in the cycle", - usage = "CYCLICAL" - ) - public static final SelectionStrategy CYCLICAL = new CyclicalSelectionStrategy("CYCLICAL", false); - - @HelpText( - doc = "Select the next at random", - usage = "RANDOM" - ) - public static final SelectionStrategy RANDOM = new RandomSelectionStrategy("RANDOM", false); - - public static SelectionStrategy valueOf(final String s) { - if (!VALUES_MAP.containsKey(s)) { - String str = VALUES.stream() - .map(SelectionStrategy::toString) + public static SelectionStrategy newInstance(final String s) { + SelectionStrategySpec selectionStrategySpec = null; + try { + selectionStrategySpec = + SelectionStrategySpecConstants.valueOfName(s); + } catch (IllegalArgumentException e) { + String str = SelectionStrategySpecConstants.values().stream() + .map(SelectionStrategySpec::getName) .collect(Collectors.joining(", ")); throw new IllegalArgumentException(String.format( - "expected selection strategy must be one of the " - + "following values: %s. actual value is %s", + "expected selection strategy must be one of the following " + + "values: %s. actual value is %s", str, - s)); + s)); } - return VALUES_MAP.get(s); + return selectionStrategySpec.newSelectionStrategy(); } - public static List values() { - return Collections.unmodifiableList(VALUES); - } + private final String name; + private final SelectionStrategySpec selectionStrategySpec; - private final boolean mutable; - private final String string; - - SelectionStrategy(final String str, final boolean mut) { - if (!mut) { - VALUES.add(this); - VALUES_MAP.put(str, this); - } - this.mutable = mut; - this.string = str; + public SelectionStrategy(final SelectionStrategySpec spec) { + this.name = spec.getName(); + this.selectionStrategySpec = spec; } - + @Override - public final boolean equals(Object obj) { + public boolean equals(Object obj) { if (this == obj) { return true; } @@ -134,46 +43,38 @@ public final boolean equals(Object obj) { return false; } SelectionStrategy other = (SelectionStrategy) obj; - if (this.string == null) { - if (other.string != null) { + if (this.name == null) { + if (other.name != null) { return false; } - } else if (!this.string.equals(other.string)) { + } else if (!this.name.equals(other.name)) { return false; } return true; } + public final String getName() { + return this.name; + } + + public final SelectionStrategySpec getSelectionStrategySpec() { + return this.selectionStrategySpec; + } + @Override - public final int hashCode() { + public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((this.string == null) ? - 0 : this.string.hashCode()); + result = prime * result + ((this.name == null) ? + 0 : this.name.hashCode()); return result; } - protected abstract T implSelectFrom(final List list); - - public final boolean isMutable() { - return this.mutable; - } - - public abstract SelectionStrategy newMutableInstance(); - - public final T selectFrom(final List list) { - if (!this.mutable) { - throw new IllegalStateException("SelectionStrategy is not mutable"); - } - if (list.size() == 0) { - return null; - } - return this.implSelectFrom(list); - } + public abstract T selectFrom(final List list); @Override public final String toString() { - return this.string; + return this.name; } } diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpec.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpec.java new file mode 100644 index 000000000..3f21b7f5a --- /dev/null +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpec.java @@ -0,0 +1,61 @@ +package com.github.jh3nd3rs0n.jargyle.server; + +import java.util.Objects; + +public abstract class SelectionStrategySpec { + + private final String name; + + SelectionStrategySpec(final String n) { + Objects.requireNonNull(n); + this.name = n; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (this.getClass() != obj.getClass()) { + return false; + } + SelectionStrategySpec other = (SelectionStrategySpec) obj; + if (this.name == null) { + if (other.name != null) { + return false; + } + } else if (!this.name.equals(other.name)) { + return false; + } + return true; + } + + public final String getName() { + return this.name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((this.name == null) ? + 0 : this.name.hashCode()); + return result; + } + + public abstract SelectionStrategy newSelectionStrategy(); + + @Override + public final String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(this.getClass().getSimpleName()) + .append(" [name=") + .append(this.name) + .append("]"); + return builder.toString(); + } + +} diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpecConstants.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpecConstants.java new file mode 100644 index 000000000..457be749e --- /dev/null +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpecConstants.java @@ -0,0 +1,74 @@ +package com.github.jh3nd3rs0n.jargyle.server; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.github.jh3nd3rs0n.jargyle.internal.annotation.HelpText; +import com.github.jh3nd3rs0n.jargyle.server.internal.selectionstrategy.impl.CyclicalSelectionStrategy; +import com.github.jh3nd3rs0n.jargyle.server.internal.selectionstrategy.impl.RandomSelectionStrategy; + +public final class SelectionStrategySpecConstants { + + private static final SelectionStrategySpecs SELECTION_STRATEGY_SPECS = + new SelectionStrategySpecs(); + + @HelpText( + doc = "Select the next in the cycle", + usage = "CYCLICAL" + ) + public static final SelectionStrategySpec CYCLICAL = SELECTION_STRATEGY_SPECS.addThenGet(new SelectionStrategySpec( + "CYCLICAL") { + + @Override + public SelectionStrategy newSelectionStrategy() { + return new CyclicalSelectionStrategy(this); + } + + }); + + @HelpText( + doc = "Select the next at random", + usage = "RANDOM" + ) + public static final SelectionStrategySpec RANDOM = SELECTION_STRATEGY_SPECS.addThenGet(new SelectionStrategySpec( + "RANDOM") { + + @Override + public SelectionStrategy newSelectionStrategy() { + return new RandomSelectionStrategy(this); + } + + }); + + private static final List VALUES = + SELECTION_STRATEGY_SPECS.toList(); + + private static final Map VALUES_MAP = + SELECTION_STRATEGY_SPECS.toMap(); + + public static SelectionStrategySpec valueOfName(final String name) { + if (VALUES_MAP.containsKey(name)) { + return VALUES_MAP.get(name); + } + String str = VALUES.stream() + .map(SelectionStrategySpec::getName) + .collect(Collectors.joining(", ")); + throw new IllegalArgumentException(String.format( + "expected selection strategy spec must be one of the " + + "following values: %s. actual value is %s", + str, + name)); + } + + public static List values() { + return VALUES; + } + + public static Map valuesMap() { + return VALUES_MAP; + } + + private SelectionStrategySpecConstants() { } + +} diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpecs.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpecs.java new file mode 100644 index 000000000..aa1b3bee4 --- /dev/null +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/SelectionStrategySpecs.java @@ -0,0 +1,34 @@ +package com.github.jh3nd3rs0n.jargyle.server; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +final class SelectionStrategySpecs { + + private final List selectionStrategySpecs; + private final Map selectionStrategySpecsMap; + + public SelectionStrategySpecs() { + this.selectionStrategySpecs = new ArrayList(); + this.selectionStrategySpecsMap = + new HashMap(); + } + + public SelectionStrategySpec addThenGet(final SelectionStrategySpec value) { + this.selectionStrategySpecs.add(value); + this.selectionStrategySpecsMap.put(value.getName(), value); + return value; + } + + public List toList() { + return Collections.unmodifiableList(this.selectionStrategySpecs); + } + + public Map toMap() { + return Collections.unmodifiableMap(this.selectionStrategySpecsMap); + } + +} diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/ruleresultspec/impl/SelectionStrategyRuleResultSpec.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/ruleresultspec/impl/SelectionStrategyRuleResultSpec.java index dc5a99342..cf46044e9 100644 --- a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/ruleresultspec/impl/SelectionStrategyRuleResultSpec.java +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/ruleresultspec/impl/SelectionStrategyRuleResultSpec.java @@ -14,8 +14,7 @@ public SelectionStrategyRuleResultSpec(final String n) { @Override public RuleResult newRuleResultOfParsableValue( final String value) { - return super.newRuleResult( - SelectionStrategy.valueOf(value).newMutableInstance()); + return super.newRuleResult(SelectionStrategy.newInstance(value)); } } diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/selectionstrategy/impl/CyclicalSelectionStrategy.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/selectionstrategy/impl/CyclicalSelectionStrategy.java new file mode 100644 index 000000000..0b60fc8bc --- /dev/null +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/selectionstrategy/impl/CyclicalSelectionStrategy.java @@ -0,0 +1,35 @@ +package com.github.jh3nd3rs0n.jargyle.server.internal.selectionstrategy.impl; + +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import com.github.jh3nd3rs0n.jargyle.server.SelectionStrategy; +import com.github.jh3nd3rs0n.jargyle.server.SelectionStrategySpec; + +public final class CyclicalSelectionStrategy extends SelectionStrategy { + + private int index; + private final ReentrantLock lock; + + public CyclicalSelectionStrategy(final SelectionStrategySpec spec) { + super(spec); + this.index = 0; + this.lock = new ReentrantLock(); + } + + public T selectFrom(final List list) { + T selected = null; + this.lock.lock(); + try { + if (this.index > list.size() - 1) { + this.index = 0; + } + selected = list.get(this.index); + this.index++; + } finally { + this.lock.unlock(); + } + return selected; + } + +} diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/selectionstrategy/impl/RandomSelectionStrategy.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/selectionstrategy/impl/RandomSelectionStrategy.java new file mode 100644 index 000000000..51556495f --- /dev/null +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/selectionstrategy/impl/RandomSelectionStrategy.java @@ -0,0 +1,31 @@ +package com.github.jh3nd3rs0n.jargyle.server.internal.selectionstrategy.impl; + +import java.security.SecureRandom; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import com.github.jh3nd3rs0n.jargyle.server.SelectionStrategy; +import com.github.jh3nd3rs0n.jargyle.server.SelectionStrategySpec; + +public final class RandomSelectionStrategy extends SelectionStrategy { + + private final ReentrantLock lock; + private final SecureRandom secureRandom; + + public RandomSelectionStrategy(final SelectionStrategySpec spec) { + super(spec); + this.lock = new ReentrantLock(); + this.secureRandom = new SecureRandom(); + } + + public T selectFrom(final List list) { + T selected = null; + this.lock.lock(); + try { + selected = list.get(this.secureRandom.nextInt(list.size())); + } finally { + this.lock.unlock(); + } + return selected; + } +} diff --git a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/settingspec/impl/SelectionStrategySettingSpec.java b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/settingspec/impl/SelectionStrategySettingSpec.java index c88a8b6b3..c797ee874 100644 --- a/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/settingspec/impl/SelectionStrategySettingSpec.java +++ b/jargyle-server/src/main/java/com/github/jh3nd3rs0n/jargyle/server/internal/settingspec/impl/SelectionStrategySettingSpec.java @@ -15,8 +15,7 @@ public SelectionStrategySettingSpec( @Override public Setting newSettingOfParsableValue( final String value) { - return super.newSetting( - SelectionStrategy.valueOf(value).newMutableInstance()); + return super.newSetting(SelectionStrategy.newInstance(value)); } }