77: Make NenjimHub the runtime owner and replace NenjimApplication with NenjimProcess

This commit is contained in:
2026-09-02 15:59:56 +02:00
parent a252c256c4
commit f5fc5bb3b5
29 changed files with 1012 additions and 461 deletions
@@ -15,7 +15,7 @@ import com.r35157.libs.valuetypes.basic.MoneyAmount;
import com.r35157.libs.valuetypes.basic.TradingPair;
import com.r35157.assetaz.services.ticker.PriceObservation;
import com.r35157.assetaz.services.ticker.TickerService;
import com.r35157.nenjim.component.NenjimApplication;
import com.r35157.nenjim.process.NenjimProcess;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -46,7 +46,7 @@ import java.util.concurrent.ConcurrentHashMap;
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.SPL_TOKEN_PROGRAM;
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.TOKEN_2022_PROGRAM;
public class EvelynImpl implements Evelyn, NenjimApplication {
public class EvelynImpl implements Evelyn, NenjimProcess {
public EvelynImpl(
@NotNull String instanceName,
@@ -18,7 +18,7 @@ import com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram;
import com.r35157.libs.valuetypes.basic.AssetPrice;
import com.r35157.libs.valuetypes.basic.MoneyAmount;
import com.r35157.libs.valuetypes.basic.TradingPair;
import com.r35157.nenjim.component.NenjimApplication;
import com.r35157.nenjim.process.NenjimProcess;
import com.r35157.service.notification.BoundNotificationService;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
@@ -49,7 +49,7 @@ import static com.r35157.assetaz.services.cis.ExternalCurrencyReference.SOLANA_M
* Reference implementation of the Evelyn IOU buyback-and-burn lifecycle.
*/
public final class EvelynIOUBurnerServiceImpl
implements EvelynIOUBurnerService, NenjimApplication {
implements EvelynIOUBurnerService, NenjimProcess {
/**
* Creates an Evelyn IOU burner with all runtime dependencies and current
* operating policy.
@@ -6,7 +6,7 @@ import com.r35157.assetaz.services.ticker.pricesource.PriceSource;
import com.r35157.assetaz.services.ticker.TickerService;
import com.r35157.libs.valuetypes.basic.AssetPrice;
import com.r35157.libs.valuetypes.basic.TradingPair;
import com.r35157.nenjim.component.NenjimApplication;
import com.r35157.nenjim.process.NenjimProcess;
import com.r35157.nenjim.service.registry.NenjimRegistryService;
import com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId;
import org.jetbrains.annotations.NotNull;
@@ -42,7 +42,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
public final class TickerServiceImpl implements
TickerService,
PriceSink,
NenjimApplication {
NenjimProcess {
public TickerServiceImpl(
@NotNull NenjimRegistryService registryService,
@NotNull NenjimComponentId... priceSourceIds) {
@@ -4,7 +4,7 @@ import com.fanitas.evelyn.core.Evelyn;
import com.fanitas.evelyn.core.EvelynStatusIndexPoint;
import com.r35157.evelyn.emc.EvelynMissionControl;
import com.r35157.libs.javafx.JavaFxRuntime;
import com.r35157.nenjim.component.NenjimApplication;
import com.r35157.nenjim.process.NenjimProcess;
import org.jetbrains.annotations.NotNull;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
@@ -30,7 +30,7 @@ import java.util.function.Function;
public final class EvelynMissionControlImpl implements
EvelynMissionControl,
NenjimApplication {
NenjimProcess {
public EvelynMissionControlImpl(
@NotNull Evelyn evelynServiceProd,
@@ -3,7 +3,7 @@ package com.r35157.jupiterperpsalarm.impl.ref;
import com.r35157.cryptowallet.solana.SolanaWallet;
import com.r35157.jupiterperpsalarm.JupiterPerpsAlarm;
import com.r35157.libs.jupiter.perps.JupiterPerpsService;
import com.r35157.nenjim.component.NenjimApplication;
import com.r35157.nenjim.process.NenjimProcess;
import org.jetbrains.annotations.NotNull;
import java.net.URI;
@@ -18,7 +18,7 @@ import java.util.concurrent.CountDownLatch;
public final class JupiterPerpsAlarmImpl implements
JupiterPerpsAlarm,
NenjimApplication {
NenjimProcess {
public JupiterPerpsAlarmImpl(
@NotNull SolanaWallet solanaWallet,
@@ -1,6 +0,0 @@
package com.r35157.nenjim.component;
/** A Nenjim component with an active start lifecycle. */
public interface NenjimApplication extends NenjimComponent {
void start() throws Exception;
}
@@ -1,7 +1,6 @@
package com.r35157.nenjim.hubd;
import com.r35157.nenjim.component.NenjimApplication;
import com.r35157.nenjim.service.registry.impl.ref.NenjimRegistryServiceManagerImpl;
import com.r35157.nenjim.hubd.impl.ref.NenjimHubImpl;
import org.jetbrains.annotations.NotNull;
public final class Main {
@@ -9,8 +8,7 @@ public final class Main {
}
public static void main(@NotNull String[] args) throws Exception {
NenjimApplication registryServiceManager =
new NenjimRegistryServiceManagerImpl();
registryServiceManager.start();
NenjimHub nenjimHub = new NenjimHubImpl();
nenjimHub.start();
}
}
@@ -0,0 +1,21 @@
package com.r35157.nenjim.hubd;
import com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/** Global owner of one Nenjim runtime's composition and process lifecycle. */
public interface NenjimHub {
void start() throws Exception;
/**
* Returns an immutable point-in-time snapshot in successful startup order.
*
* <p>An ID is present only when that process's {@code start()} invocation
* returned successfully. Presence makes no health or continued-liveness
* guarantee.</p>
*/
@NotNull
List<NenjimComponentId> getStartedProcessIds();
}
@@ -0,0 +1,372 @@
package com.r35157.nenjim.hubd.impl.ref;
import com.fanitas.evelyn.core.Evelyn;
import com.fanitas.evelyn.core.impl.ref.EvelynImpl;
import com.fanitas.evelyn.service.burner.EvelynIOUBurnerService;
import com.fanitas.evelyn.service.burner.impl.ref.EvelynIOUBurnerServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.r35157.assetaz.services.cis.CurrencyTypeIds;
import com.r35157.assetaz.services.ticker.pricesource.PriceSource;
import com.r35157.assetaz.services.ticker.TickerService;
import com.r35157.assetaz.services.ticker.pricesource.impl.hardcoded.HardcodedPriceSource;
import com.r35157.assetaz.services.ticker.pricesource.impl.raydiumpool.RaydiumPoolPriceSource;
import com.r35157.assetaz.services.ticker.impl.ref.TickerServiceImpl;
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
import com.r35157.assetaz.services.cis.impl.hc.HardcodedCurrencyIdentityService;
import com.r35157.cryptowallet.solana.SolanaWallet;
import com.r35157.cryptowallet.solana.impl.ref.SolanaWalletImpl;
import com.r35157.evelyn.emc.EvelynMissionControl;
import com.r35157.evelyn.emc.impl.ref.EvelynMissionControlImpl;
import com.r35157.jupiterperpsalarm.JupiterPerpsAlarm;
import com.r35157.jupiterperpsalarm.impl.ref.JupiterPerpsAlarmImpl;
import com.r35157.libs.jupiter.perps.JupiterPerpsService;
import com.r35157.libs.jupiter.perps.impl.anchoridl.AnchorIdlJupiterPerpsServiceImpl;
import com.r35157.libs.jupiter.swap.JupiterSwapService;
import com.r35157.libs.jupiter.swap.impl.ref.JupiterSwapServiceImpl;
import com.r35157.libs.objcache.ObjectCache;
import com.r35157.libs.objcache.impl.ref.ObjectCacheImpl;
import com.r35157.libs.solana.impl.cached.CachedSolanaBlockChain;
import com.r35157.libs.valuetypes.basic.MoneyAmount;
import com.r35157.nenjim.hubd.NenjimHub;
import com.r35157.nenjim.process.NenjimProcess;
import com.r35157.nenjim.service.registry.NenjimRegistryService;
import com.r35157.nenjim.service.registry.NenjimRegistryServiceManager;
import com.r35157.nenjim.service.registry.impl.ref.NenjimRegistryAssembly;
import com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId;
import com.r35157.libs.raydium.Raydium;
import com.r35157.libs.raydium.impl.ref.RaydiumImpl;
import com.r35157.libs.solana.SolanaBlockChain;
import com.r35157.libs.solana.impl.ref.SolanaBlockChainImpl;
import com.r35157.libs.valuetypes.basic.TradingPair;
import com.r35157.service.notification.BoundNotificationService;
import com.r35157.service.notification.impl.discord.DiscordNotificationServiceImpl;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.net.URI;
import java.net.http.HttpClient;
import java.time.Clock;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.EVE_ID;
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.USDT_ID;
/** Reference composition root and runtime lifecycle owner. */
public final class NenjimHubImpl implements NenjimHub {
@Override
public void start() throws Exception {
if (!startAttempted.compareAndSet(false, true)) {
throw new IllegalStateException(
"NenjimHub start has already been attempted");
}
log.info("Initializing Nenjim Registry and components...");
NenjimRegistryAssembly registryAssembly =
NenjimRegistryAssembly.create();
NenjimRegistryService registryService =
registryAssembly.registryService();
NenjimProcess registryProcess = registryAssembly.registryProcess();
registryServiceManager = registryAssembly.registryServiceManager();
registryProcess.start();
registryServiceManager.registerComponent(
REGISTRY_SERVICE_ID,
registryService);
recordStartedProcess(REGISTRY_SERVICE_ID);
initializeAndRegisterComponents(
registryService,
registryServiceManager);
log.info("Starting explicitly enabled Nenjim processes...");
startProcess(registryService, TICKER_SERVICE_ID);
startProcess(registryService, EVELYN_SERVICE_PROD_ID);
startProcess(registryService, EVELYN_SERVICE_TEST_ID);
startProcess(registryService, EVELYN_IOU_BURNER_PROD_ID);
startProcess(registryService, EVELYN_MISSION_CONTROL_ID);
System.out.println("Done - Now online!");
waitForAndShutdown();
}
@Override
public synchronized @NotNull List<NenjimComponentId>
getStartedProcessIds() {
return List.copyOf(startedProcessIds);
}
private void initializeAndRegisterComponents(
NenjimRegistryService registryService,
NenjimRegistryServiceManager registryServiceManager) {
initializeBuildingBlocks(registryServiceManager);
initializePriceSources(registryServiceManager);
initializeServices(registryService, registryServiceManager);
initializeApplications(registryServiceManager);
}
private void initializeBuildingBlocks(
NenjimRegistryServiceManager registryServiceManager) {
objectCache = new ObjectCacheImpl();
registryServiceManager.registerComponent(OBJECT_CACHE_ID, objectCache);
clockUTC = Clock.systemUTC();
cis = new HardcodedCurrencyIdentityService();
registryServiceManager.registerComponent(CURRENCY_IDENTITY_SERVICE_ID, cis);
httpClient = HttpClient.newHttpClient();
objectMapper = new ObjectMapper();
solanaBlockChain = new SolanaBlockChainImpl(httpClient, objectMapper, cis);
registryServiceManager.registerComponent(SOLANA_BLOCKCHAIN_DIRECT_ID, solanaBlockChain);
cachedSolanaBlockChain = new CachedSolanaBlockChain(solanaBlockChain, objectCache);
registryServiceManager.registerComponent(SOLANA_BLOCKCHAIN_CACHED_ID, cachedSolanaBlockChain);
raydium = new RaydiumImpl(httpClient, objectMapper, cachedSolanaBlockChain, cis);
registryServiceManager.registerComponent(RAYDIUM_SERVICE_ID, raydium);
eveUSDT = new TradingPair(cis.resolve(EVE_ID), cis.resolve(USDT_ID));
solanaWalletEvelynPerpsTest = new SolanaWalletImpl(
"6NyYEoXmJyBXvU4pvv9aoxZNdyqqtkmPBZFEDkRDHcqp",
"evelyn_perps_test",
solanaBlockChain
);
registryServiceManager.registerComponent(
SOLANA_WALLET_EVELYN_PERPS_TEST_ID,
solanaWalletEvelynPerpsTest);
solanaWalletEvelynPerpsProd = new SolanaWalletImpl(
"H5Yrdm7B4FHhTxhPw9SVwFzQyTEL6bAPptZuTyRxcepq",
"evelyn_perps_prod",
solanaBlockChain
);
registryServiceManager.registerComponent(
SOLANA_WALLET_EVELYN_PERPS_PROD_ID,
solanaWalletEvelynPerpsProd);
solanaWalletEvelynBurnerProd = new SolanaWalletImpl(
"5kjVTc1rebYHg56NyTxSSN1XMKqT2E81eWiaSx1CmzmV",
"evelyn_burner_prod",
solanaBlockChain
);
registryServiceManager.registerComponent(
SOLANA_WALLET_EVELYN_BURNER_PROD_ID,
solanaWalletEvelynBurnerProd);
}
private void initializePriceSources(
NenjimRegistryServiceManager registryServiceManager) {
hardcodedPriceSource = new HardcodedPriceSource(cis);
registryServiceManager.registerComponent(
HARDCODED_PRICE_SOURCE_ID,
hardcodedPriceSource);
raydiumPoolPriceSource = new RaydiumPoolPriceSource(raydium, EVE_USDT_RAYDIUM_POOL_ID, eveUSDT);
registryServiceManager.registerComponent(
RAYDIUM_POOL_PRICE_SOURCE_ID,
raydiumPoolPriceSource);
}
private void initializeServices(
NenjimRegistryService registryService,
NenjimRegistryServiceManager registryServiceManager) {
tickerService = new TickerServiceImpl(
registryService,
RAYDIUM_POOL_PRICE_SOURCE_ID
);
registryServiceManager.registerComponent(TICKER_SERVICE_ID, tickerService);
jupiterServicePerpsProd = new AnchorIdlJupiterPerpsServiceImpl(
cachedSolanaBlockChain,
solanaWalletEvelynPerpsProd,
cis
);
registryServiceManager.registerComponent(
JUPITER_PERPS_EVELYN_PROD_ID,
jupiterServicePerpsProd);
jupiterServicePerpsTest = new AnchorIdlJupiterPerpsServiceImpl(
cachedSolanaBlockChain,
solanaWalletEvelynPerpsTest,
cis
);
registryServiceManager.registerComponent(
JUPITER_PERPS_EVELYN_TEST_ID,
jupiterServicePerpsTest);
jupiterPerpsAlarm = new JupiterPerpsAlarmImpl(
solanaWalletEvelynPerpsTest,
jupiterServicePerpsTest,
"--config=conf/alarms.conf"
);
registryServiceManager.registerComponent(
JUPITER_PERPS_ALARM_ID,
jupiterPerpsAlarm);
evelynServiceProd = new EvelynImpl("Production", tickerService, eveUSDT, clockUTC);
registryServiceManager.registerComponent(EVELYN_SERVICE_PROD_ID, evelynServiceProd);
evelynServiceTest = new EvelynImpl("Test", tickerService, eveUSDT, clockUTC);
registryServiceManager.registerComponent(EVELYN_SERVICE_TEST_ID, evelynServiceTest);
jupiterSwapService = new JupiterSwapServiceImpl(solanaBlockChain, solanaWalletEvelynBurnerProd);
registryServiceManager.registerComponent(
JUPITER_SWAP_EVELYN_BURNER_PROD_ID,
jupiterSwapService);
discordAssetAZTokenNotificationService = new DiscordNotificationServiceImpl(
URI.create("https://discordapp.com/api/webhooks/1537799046025187458/RtoNR5HnGmnutsMfM8AKP8mLDVqP1AxpEQ3p2IvBiaqVOjuVWNcShMkIh2D9uT27Mt4A"),
"DiscordBot (https://git.r35157.com/r35157/com_r35157_nenjim-hubd-impl_ref, 0.1)",
Duration.ofSeconds(10)
);
registryServiceManager.registerComponent(
DISCORD_ASSETAZ_TOKEN_NOTIFICATION_ID,
discordAssetAZTokenNotificationService);
List<MoneyAmount> burnTokenCandidates = List.of(new MoneyAmount(new ΩAmountΩ("0.1"), cis.resolve(CurrencyTypeIds.USDT_ID)));
ΩUSDCAmountΩ maximumSwapAmountPerIterationInUSD = new ΩUSDCAmountΩ("0.25");
evelynIOUBurnerServiceProd = new EvelynIOUBurnerServiceImpl(
solanaWalletEvelynBurnerProd,
jupiterSwapService,
solanaBlockChain,
tickerService,
cis,
discordAssetAZTokenNotificationService,
burnTokenCandidates,
maximumSwapAmountPerIterationInUSD,
200,
2*1000*60L,
Duration.ofSeconds(10),
Duration.ofHours(1)
);
registryServiceManager.registerComponent(
EVELYN_IOU_BURNER_PROD_ID,
evelynIOUBurnerServiceProd);
}
private void initializeApplications(
NenjimRegistryServiceManager registryServiceManager) {
missionControl = new EvelynMissionControlImpl(evelynServiceProd, evelynServiceTest);
registryServiceManager.registerComponent(
EVELYN_MISSION_CONTROL_ID,
missionControl);
}
private void startProcess(
NenjimRegistryService registryService,
NenjimComponentId componentId) throws Exception {
NenjimProcess process = registryService.getComponent(
componentId,
NenjimProcess.class);
if (process == null) {
throw new IllegalStateException(
"Required Nenjim process '" + componentId.value()
+ "' is not registered as "
+ NenjimProcess.class.getName());
}
process.start();
recordStartedProcess(componentId);
}
private synchronized void recordStartedProcess(
NenjimComponentId componentId) {
startedProcessIds.add(componentId);
}
private void waitForAndShutdown() throws InterruptedException {
shutdownLatch.await();
System.out.println("Nenjim is now shut down!");
}
private static final Logger log = LoggerFactory.getLogger(
NenjimHubImpl.class);
private static final NenjimComponentId REGISTRY_SERVICE_ID =
new NenjimComponentId("nenjim.registry.service");
private static final NenjimComponentId OBJECT_CACHE_ID =
new NenjimComponentId("nenjim.object-cache.default");
private static final NenjimComponentId CURRENCY_IDENTITY_SERVICE_ID =
new NenjimComponentId("assetaz.currency-identity.hardcoded");
private static final NenjimComponentId SOLANA_BLOCKCHAIN_DIRECT_ID =
new NenjimComponentId("solana.blockchain.direct");
private static final NenjimComponentId SOLANA_BLOCKCHAIN_CACHED_ID =
new NenjimComponentId("solana.blockchain.cached");
private static final NenjimComponentId RAYDIUM_SERVICE_ID =
new NenjimComponentId("raydium.service.default");
private static final NenjimComponentId SOLANA_WALLET_EVELYN_PERPS_TEST_ID =
new NenjimComponentId("solana.wallet.evelyn-perps-test");
private static final NenjimComponentId SOLANA_WALLET_EVELYN_PERPS_PROD_ID =
new NenjimComponentId("solana.wallet.evelyn-perps-prod");
private static final NenjimComponentId SOLANA_WALLET_EVELYN_BURNER_PROD_ID =
new NenjimComponentId("solana.wallet.evelyn-burner-prod");
private static final NenjimComponentId HARDCODED_PRICE_SOURCE_ID =
new NenjimComponentId("assetaz.price-source.hardcoded");
private static final NenjimComponentId RAYDIUM_POOL_PRICE_SOURCE_ID =
new NenjimComponentId(
"assetaz.price-source.raydium-pool.eve-usdt");
private static final NenjimComponentId TICKER_SERVICE_ID =
new NenjimComponentId("assetaz.ticker.default");
private static final NenjimComponentId JUPITER_PERPS_EVELYN_PROD_ID =
new NenjimComponentId("jupiter.perps.evelyn-prod");
private static final NenjimComponentId JUPITER_PERPS_EVELYN_TEST_ID =
new NenjimComponentId("jupiter.perps.evelyn-test");
private static final NenjimComponentId JUPITER_PERPS_ALARM_ID =
new NenjimComponentId("jupiter-perps-alarm.default");
private static final NenjimComponentId EVELYN_SERVICE_PROD_ID =
new NenjimComponentId("evelyn.service.prod");
private static final NenjimComponentId EVELYN_SERVICE_TEST_ID =
new NenjimComponentId("evelyn.service.test");
private static final NenjimComponentId JUPITER_SWAP_EVELYN_BURNER_PROD_ID =
new NenjimComponentId("jupiter.swap.evelyn-burner-prod");
private static final NenjimComponentId DISCORD_ASSETAZ_TOKEN_NOTIFICATION_ID =
new NenjimComponentId("notification.discord.assetaz-token");
private static final NenjimComponentId EVELYN_IOU_BURNER_PROD_ID =
new NenjimComponentId("evelyn.iou-burner.prod");
private static final NenjimComponentId EVELYN_MISSION_CONTROL_ID =
new NenjimComponentId("evelyn.mission-control.default");
private static final ΩRaydiumLiquidityPoolIdΩ EVE_USDT_RAYDIUM_POOL_ID =
"8rN4BTEzbogQosEQYgsEu18XwfKS5Yoxqwit8zEVFwEe";
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
private final AtomicBoolean startAttempted = new AtomicBoolean();
private final List<NenjimComponentId> startedProcessIds = new ArrayList<>();
private NenjimRegistryServiceManager registryServiceManager;
// Dependency components
private ObjectCache objectCache;
private Clock clockUTC;
private CurrencyIdentityService cis;
private SolanaBlockChain solanaBlockChain;
private SolanaBlockChain cachedSolanaBlockChain;
private Raydium raydium;
private TradingPair eveUSDT;
private HttpClient httpClient;
private ObjectMapper objectMapper;
private SolanaWallet solanaWalletEvelynPerpsProd;
private SolanaWallet solanaWalletEvelynPerpsTest;
private SolanaWallet solanaWalletEvelynBurnerProd;
private JupiterPerpsService jupiterServicePerpsProd;
private JupiterPerpsService jupiterServicePerpsTest;
// Price-source components
private PriceSource hardcodedPriceSource;
private PriceSource raydiumPoolPriceSource;
// Services
private TickerService tickerService;
private JupiterPerpsAlarm jupiterPerpsAlarm;
private Evelyn evelynServiceProd;
private Evelyn evelynServiceTest;
private JupiterSwapService jupiterSwapService;
private BoundNotificationService discordAssetAZTokenNotificationService;
private EvelynIOUBurnerService evelynIOUBurnerServiceProd;
// Application components
private EvelynMissionControl missionControl;
}
@@ -0,0 +1,8 @@
package com.r35157.nenjim.process;
import com.r35157.nenjim.component.NenjimComponent;
/** A Nenjim component with an active Hub-managed start lifecycle. */
public interface NenjimProcess extends NenjimComponent {
void start() throws Exception;
}
@@ -1,8 +1,12 @@
package com.r35157.nenjim.service.registry;
import com.r35157.nenjim.component.NenjimComponent;
import com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId;
import org.jetbrains.annotations.NotNull;
/** Lifecycle owner for the Nenjim Registry and hardcoded component graph. */
public interface NenjimRegistryServiceManager extends NenjimComponent {
void start() throws Exception;
/** Passive registration handle retained privately by the NenjimHub. */
public interface NenjimRegistryServiceManager {
void registerComponent(
@NotNull NenjimComponentId componentId,
@NotNull NenjimComponent component);
}
@@ -0,0 +1,48 @@
package com.r35157.nenjim.service.registry.impl.ref;
import com.r35157.nenjim.process.NenjimProcess;
import com.r35157.nenjim.service.registry.NenjimRegistryService;
import com.r35157.nenjim.service.registry.NenjimRegistryServiceManager;
import org.jetbrains.annotations.NotNull;
/**
* Implementation-only construction boundary for one Registry subsystem.
*
* <p>This helper owns no runtime lifecycle and exposes no administrative
* Registry view. In particular, a consumer holding only
* {@link NenjimRegistryService} cannot use that service reference to derive
* the manager bound to the active Registry.</p>
*/
public final class NenjimRegistryAssembly {
private NenjimRegistryAssembly(
@NotNull NenjimRegistryServiceImpl registry) {
registryService = registry;
registryProcess = registry;
registryServiceManager = new NenjimRegistryServiceManagerImpl(
registry);
}
/** Creates mutually consistent public views of one new Registry. */
public static @NotNull NenjimRegistryAssembly create() {
return new NenjimRegistryAssembly(new NenjimRegistryServiceImpl());
}
/** Returns the ordinary read-only service view. */
public @NotNull NenjimRegistryService registryService() {
return registryService;
}
/** Returns the process view of the same object as the service view. */
public @NotNull NenjimProcess registryProcess() {
return registryProcess;
}
/** Returns the distinct passive manager bound to this Registry. */
public @NotNull NenjimRegistryServiceManager registryServiceManager() {
return registryServiceManager;
}
private final NenjimRegistryService registryService;
private final NenjimProcess registryProcess;
private final NenjimRegistryServiceManager registryServiceManager;
}
@@ -4,7 +4,16 @@ import com.r35157.nenjim.component.NenjimComponent;
import com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId;
import org.jetbrains.annotations.NotNull;
/** Internal mutation handle retained only by the Registry service manager. */
/**
* Internal mutation handle retained only by the Registry service manager.
*
* <p>The same Registry object is exposed publicly through
* {@link com.r35157.nenjim.service.registry.NenjimRegistryService}. This
* interface must remain package-private: making it public would allow ordinary
* consumers to cast that service object to the mutation interface and would
* break the Registry's read-only consumer contract. It must never be exposed
* outside this reference implementation package.</p>
*/
interface NenjimRegistryServiceAdmin {
void registerComponent(
@NotNull NenjimComponentId componentId,
@@ -1,7 +1,7 @@
package com.r35157.nenjim.service.registry.impl.ref;
import com.r35157.nenjim.component.NenjimApplication;
import com.r35157.nenjim.component.NenjimComponent;
import com.r35157.nenjim.process.NenjimProcess;
import com.r35157.nenjim.service.registry.NenjimRegistryService;
import com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId;
import org.jetbrains.annotations.NotNull;
@@ -19,7 +19,7 @@ import java.util.Set;
final class NenjimRegistryServiceImpl implements
NenjimRegistryService,
NenjimRegistryServiceAdmin,
NenjimApplication {
NenjimProcess {
@Override
public synchronized void start() {
if (startAttempted) {
@@ -1,351 +1,28 @@
package com.r35157.nenjim.service.registry.impl.ref;
import com.fanitas.evelyn.core.Evelyn;
import com.fanitas.evelyn.core.impl.ref.EvelynImpl;
import com.fanitas.evelyn.service.burner.EvelynIOUBurnerService;
import com.fanitas.evelyn.service.burner.impl.ref.EvelynIOUBurnerServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.r35157.assetaz.services.cis.CurrencyTypeIds;
import com.r35157.assetaz.services.ticker.pricesource.PriceSource;
import com.r35157.assetaz.services.ticker.TickerService;
import com.r35157.assetaz.services.ticker.pricesource.impl.hardcoded.HardcodedPriceSource;
import com.r35157.assetaz.services.ticker.pricesource.impl.raydiumpool.RaydiumPoolPriceSource;
import com.r35157.assetaz.services.ticker.impl.ref.TickerServiceImpl;
import com.r35157.assetaz.services.cis.CurrencyIdentityService;
import com.r35157.assetaz.services.cis.impl.hc.HardcodedCurrencyIdentityService;
import com.r35157.cryptowallet.solana.SolanaWallet;
import com.r35157.cryptowallet.solana.impl.ref.SolanaWalletImpl;
import com.r35157.evelyn.emc.EvelynMissionControl;
import com.r35157.evelyn.emc.impl.ref.EvelynMissionControlImpl;
import com.r35157.jupiterperpsalarm.JupiterPerpsAlarm;
import com.r35157.jupiterperpsalarm.impl.ref.JupiterPerpsAlarmImpl;
import com.r35157.libs.jupiter.perps.JupiterPerpsService;
import com.r35157.libs.jupiter.perps.impl.anchoridl.AnchorIdlJupiterPerpsServiceImpl;
import com.r35157.libs.jupiter.swap.JupiterSwapService;
import com.r35157.libs.jupiter.swap.impl.ref.JupiterSwapServiceImpl;
import com.r35157.libs.objcache.ObjectCache;
import com.r35157.libs.objcache.impl.ref.ObjectCacheImpl;
import com.r35157.libs.solana.impl.cached.CachedSolanaBlockChain;
import com.r35157.libs.valuetypes.basic.MoneyAmount;
import com.r35157.nenjim.component.NenjimApplication;
import com.r35157.nenjim.service.registry.NenjimRegistryService;
import com.r35157.nenjim.component.NenjimComponent;
import com.r35157.nenjim.service.registry.NenjimRegistryServiceManager;
import com.r35157.nenjim.service.registry.valuetypes.NenjimComponentId;
import com.r35157.libs.raydium.Raydium;
import com.r35157.libs.raydium.impl.ref.RaydiumImpl;
import com.r35157.libs.solana.SolanaBlockChain;
import com.r35157.libs.solana.impl.ref.SolanaBlockChainImpl;
import com.r35157.libs.valuetypes.basic.TradingPair;
import com.r35157.service.notification.BoundNotificationService;
import com.r35157.service.notification.impl.discord.DiscordNotificationServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jetbrains.annotations.NotNull;
import java.math.BigDecimal;
import java.net.URI;
import java.net.http.HttpClient;
import java.time.Clock;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.EVE_ID;
import static com.r35157.assetaz.services.cis.CurrencyTypeIds.USDT_ID;
import java.util.Objects;
/** Reference passive registration handle for one Registry object. */
public final class NenjimRegistryServiceManagerImpl implements
NenjimRegistryServiceManager,
NenjimApplication {
NenjimRegistryServiceManager {
NenjimRegistryServiceManagerImpl(
@NotNull NenjimRegistryServiceAdmin registryAdmin) {
this.registryAdmin = Objects.requireNonNull(
registryAdmin,
"registryAdmin");
}
@Override
public void start() throws Exception {
if (!startAttempted.compareAndSet(false, true)) {
throw new IllegalStateException(
"Nenjim Registry service manager start has already been attempted");
}
log.info("Initializing Nenjim Registry and components...");
NenjimRegistryServiceImpl registry = new NenjimRegistryServiceImpl();
NenjimRegistryService registryService = registry;
NenjimRegistryServiceAdmin registryAdmin = registry;
NenjimApplication registryApplication = registry;
registryApplication.start();
registryAdmin.registerComponent(REGISTRY_SERVICE_ID, registry);
registryAdmin.registerComponent(REGISTRY_SERVICE_MANAGER_ID, this);
initializeAndRegisterComponents(registryService, registryAdmin);
log.info("Starting explicitly enabled Nenjim applications...");
startApplication(registryService, TICKER_SERVICE_ID);
startApplication(registryService, EVELYN_SERVICE_PROD_ID);
startApplication(registryService, EVELYN_SERVICE_TEST_ID);
startApplication(registryService, EVELYN_IOU_BURNER_PROD_ID);
startApplication(registryService, EVELYN_MISSION_CONTROL_ID);
System.out.println("Done - Now online!");
waitForAndShutdown();
public void registerComponent(
@NotNull NenjimComponentId componentId,
@NotNull NenjimComponent component) {
registryAdmin.registerComponent(componentId, component);
}
private void initializeAndRegisterComponents(
NenjimRegistryService registryService,
NenjimRegistryServiceAdmin registryAdmin) {
initializeBuildingBlocks(registryAdmin);
initializePriceSources(registryAdmin);
initializeServices(registryService, registryAdmin);
initializeApplications(registryAdmin);
}
private void initializeBuildingBlocks(
NenjimRegistryServiceAdmin registryAdmin) {
objectCache = new ObjectCacheImpl();
registryAdmin.registerComponent(OBJECT_CACHE_ID, objectCache);
clockUTC = Clock.systemUTC();
cis = new HardcodedCurrencyIdentityService();
registryAdmin.registerComponent(CURRENCY_IDENTITY_SERVICE_ID, cis);
httpClient = HttpClient.newHttpClient();
objectMapper = new ObjectMapper();
solanaBlockChain = new SolanaBlockChainImpl(httpClient, objectMapper, cis);
registryAdmin.registerComponent(SOLANA_BLOCKCHAIN_DIRECT_ID, solanaBlockChain);
cachedSolanaBlockChain = new CachedSolanaBlockChain(solanaBlockChain, objectCache);
registryAdmin.registerComponent(SOLANA_BLOCKCHAIN_CACHED_ID, cachedSolanaBlockChain);
raydium = new RaydiumImpl(httpClient, objectMapper, cachedSolanaBlockChain, cis);
registryAdmin.registerComponent(RAYDIUM_SERVICE_ID, raydium);
eveUSDT = new TradingPair(cis.resolve(EVE_ID), cis.resolve(USDT_ID));
solanaWalletEvelynPerpsTest = new SolanaWalletImpl(
"6NyYEoXmJyBXvU4pvv9aoxZNdyqqtkmPBZFEDkRDHcqp",
"evelyn_perps_test",
solanaBlockChain
);
registryAdmin.registerComponent(
SOLANA_WALLET_EVELYN_PERPS_TEST_ID,
solanaWalletEvelynPerpsTest);
solanaWalletEvelynPerpsProd = new SolanaWalletImpl(
"H5Yrdm7B4FHhTxhPw9SVwFzQyTEL6bAPptZuTyRxcepq",
"evelyn_perps_prod",
solanaBlockChain
);
registryAdmin.registerComponent(
SOLANA_WALLET_EVELYN_PERPS_PROD_ID,
solanaWalletEvelynPerpsProd);
solanaWalletEvelynBurnerProd = new SolanaWalletImpl(
"5kjVTc1rebYHg56NyTxSSN1XMKqT2E81eWiaSx1CmzmV",
"evelyn_burner_prod",
solanaBlockChain
);
registryAdmin.registerComponent(
SOLANA_WALLET_EVELYN_BURNER_PROD_ID,
solanaWalletEvelynBurnerProd);
}
private void initializePriceSources(
NenjimRegistryServiceAdmin registryAdmin) {
hardcodedPriceSource = new HardcodedPriceSource(cis);
registryAdmin.registerComponent(
HARDCODED_PRICE_SOURCE_ID,
hardcodedPriceSource);
raydiumPoolPriceSource = new RaydiumPoolPriceSource(raydium, EVE_USDT_RAYDIUM_POOL_ID, eveUSDT);
registryAdmin.registerComponent(
RAYDIUM_POOL_PRICE_SOURCE_ID,
raydiumPoolPriceSource);
}
private void initializeServices(
NenjimRegistryService registryService,
NenjimRegistryServiceAdmin registryAdmin) {
tickerService = new TickerServiceImpl(
registryService,
RAYDIUM_POOL_PRICE_SOURCE_ID
);
registryAdmin.registerComponent(TICKER_SERVICE_ID, tickerService);
jupiterServicePerpsProd = new AnchorIdlJupiterPerpsServiceImpl(
cachedSolanaBlockChain,
solanaWalletEvelynPerpsProd,
cis
);
registryAdmin.registerComponent(
JUPITER_PERPS_EVELYN_PROD_ID,
jupiterServicePerpsProd);
jupiterServicePerpsTest = new AnchorIdlJupiterPerpsServiceImpl(
cachedSolanaBlockChain,
solanaWalletEvelynPerpsTest,
cis
);
registryAdmin.registerComponent(
JUPITER_PERPS_EVELYN_TEST_ID,
jupiterServicePerpsTest);
jupiterPerpsAlarm = new JupiterPerpsAlarmImpl(
solanaWalletEvelynPerpsTest,
jupiterServicePerpsTest,
"--config=conf/alarms.conf"
);
registryAdmin.registerComponent(
JUPITER_PERPS_ALARM_ID,
jupiterPerpsAlarm);
evelynServiceProd = new EvelynImpl("Production", tickerService, eveUSDT, clockUTC);
registryAdmin.registerComponent(EVELYN_SERVICE_PROD_ID, evelynServiceProd);
evelynServiceTest = new EvelynImpl("Test", tickerService, eveUSDT, clockUTC);
registryAdmin.registerComponent(EVELYN_SERVICE_TEST_ID, evelynServiceTest);
jupiterSwapService = new JupiterSwapServiceImpl(solanaBlockChain, solanaWalletEvelynBurnerProd);
registryAdmin.registerComponent(
JUPITER_SWAP_EVELYN_BURNER_PROD_ID,
jupiterSwapService);
discordAssetAZTokenNotificationService = new DiscordNotificationServiceImpl(
URI.create("https://discordapp.com/api/webhooks/1537799046025187458/RtoNR5HnGmnutsMfM8AKP8mLDVqP1AxpEQ3p2IvBiaqVOjuVWNcShMkIh2D9uT27Mt4A"),
"DiscordBot (https://git.r35157.com/r35157/com_r35157_nenjim-hubd-impl_ref, 0.1)",
Duration.ofSeconds(10)
);
registryAdmin.registerComponent(
DISCORD_ASSETAZ_TOKEN_NOTIFICATION_ID,
discordAssetAZTokenNotificationService);
List<MoneyAmount> burnTokenCandidates = List.of(new MoneyAmount(new ΩAmountΩ("0.1"), cis.resolve(CurrencyTypeIds.USDT_ID)));
ΩUSDCAmountΩ maximumSwapAmountPerIterationInUSD = new ΩUSDCAmountΩ("0.25");
evelynIOUBurnerServiceProd = new EvelynIOUBurnerServiceImpl(
solanaWalletEvelynBurnerProd,
jupiterSwapService,
solanaBlockChain,
tickerService,
cis,
discordAssetAZTokenNotificationService,
burnTokenCandidates,
maximumSwapAmountPerIterationInUSD,
200,
2*1000*60L,
Duration.ofSeconds(10),
Duration.ofHours(1)
);
registryAdmin.registerComponent(
EVELYN_IOU_BURNER_PROD_ID,
evelynIOUBurnerServiceProd);
}
private void initializeApplications(
NenjimRegistryServiceAdmin registryAdmin) {
missionControl = new EvelynMissionControlImpl(evelynServiceProd, evelynServiceTest);
registryAdmin.registerComponent(
EVELYN_MISSION_CONTROL_ID,
missionControl);
}
private static void startApplication(
NenjimRegistryService registryService,
NenjimComponentId componentId) throws Exception {
NenjimApplication application = registryService.getComponent(
componentId,
NenjimApplication.class);
if (application == null) {
throw new IllegalStateException(
"Required Nenjim application '" + componentId.value()
+ "' is not registered as "
+ NenjimApplication.class.getName());
}
application.start();
}
private void waitForAndShutdown() throws InterruptedException {
shutdownLatch.await();
System.out.println("Nenjim is now shut down!");
}
private static final Logger log = LoggerFactory.getLogger(
NenjimRegistryServiceManagerImpl.class);
private static final NenjimComponentId REGISTRY_SERVICE_ID =
new NenjimComponentId("nenjim.registry.service");
private static final NenjimComponentId REGISTRY_SERVICE_MANAGER_ID =
new NenjimComponentId("nenjim.registry.service-manager");
private static final NenjimComponentId OBJECT_CACHE_ID =
new NenjimComponentId("nenjim.object-cache.default");
private static final NenjimComponentId CURRENCY_IDENTITY_SERVICE_ID =
new NenjimComponentId("assetaz.currency-identity.hardcoded");
private static final NenjimComponentId SOLANA_BLOCKCHAIN_DIRECT_ID =
new NenjimComponentId("solana.blockchain.direct");
private static final NenjimComponentId SOLANA_BLOCKCHAIN_CACHED_ID =
new NenjimComponentId("solana.blockchain.cached");
private static final NenjimComponentId RAYDIUM_SERVICE_ID =
new NenjimComponentId("raydium.service.default");
private static final NenjimComponentId SOLANA_WALLET_EVELYN_PERPS_TEST_ID =
new NenjimComponentId("solana.wallet.evelyn-perps-test");
private static final NenjimComponentId SOLANA_WALLET_EVELYN_PERPS_PROD_ID =
new NenjimComponentId("solana.wallet.evelyn-perps-prod");
private static final NenjimComponentId SOLANA_WALLET_EVELYN_BURNER_PROD_ID =
new NenjimComponentId("solana.wallet.evelyn-burner-prod");
private static final NenjimComponentId HARDCODED_PRICE_SOURCE_ID =
new NenjimComponentId("assetaz.price-source.hardcoded");
private static final NenjimComponentId RAYDIUM_POOL_PRICE_SOURCE_ID =
new NenjimComponentId(
"assetaz.price-source.raydium-pool.eve-usdt");
private static final NenjimComponentId TICKER_SERVICE_ID =
new NenjimComponentId("assetaz.ticker.default");
private static final NenjimComponentId JUPITER_PERPS_EVELYN_PROD_ID =
new NenjimComponentId("jupiter.perps.evelyn-prod");
private static final NenjimComponentId JUPITER_PERPS_EVELYN_TEST_ID =
new NenjimComponentId("jupiter.perps.evelyn-test");
private static final NenjimComponentId JUPITER_PERPS_ALARM_ID =
new NenjimComponentId("jupiter-perps-alarm.default");
private static final NenjimComponentId EVELYN_SERVICE_PROD_ID =
new NenjimComponentId("evelyn.service.prod");
private static final NenjimComponentId EVELYN_SERVICE_TEST_ID =
new NenjimComponentId("evelyn.service.test");
private static final NenjimComponentId JUPITER_SWAP_EVELYN_BURNER_PROD_ID =
new NenjimComponentId("jupiter.swap.evelyn-burner-prod");
private static final NenjimComponentId DISCORD_ASSETAZ_TOKEN_NOTIFICATION_ID =
new NenjimComponentId("notification.discord.assetaz-token");
private static final NenjimComponentId EVELYN_IOU_BURNER_PROD_ID =
new NenjimComponentId("evelyn.iou-burner.prod");
private static final NenjimComponentId EVELYN_MISSION_CONTROL_ID =
new NenjimComponentId("evelyn.mission-control.default");
private static final ΩRaydiumLiquidityPoolIdΩ EVE_USDT_RAYDIUM_POOL_ID =
"8rN4BTEzbogQosEQYgsEu18XwfKS5Yoxqwit8zEVFwEe";
private final CountDownLatch shutdownLatch = new CountDownLatch(1);
private final AtomicBoolean startAttempted = new AtomicBoolean();
// Dependency components
private ObjectCache objectCache;
private Clock clockUTC;
private CurrencyIdentityService cis;
private SolanaBlockChain solanaBlockChain;
private SolanaBlockChain cachedSolanaBlockChain;
private Raydium raydium;
private TradingPair eveUSDT;
private HttpClient httpClient;
private ObjectMapper objectMapper;
private SolanaWallet solanaWalletEvelynPerpsProd;
private SolanaWallet solanaWalletEvelynPerpsTest;
private SolanaWallet solanaWalletEvelynBurnerProd;
private JupiterPerpsService jupiterServicePerpsProd;
private JupiterPerpsService jupiterServicePerpsTest;
// Price-source components
private PriceSource hardcodedPriceSource;
private PriceSource raydiumPoolPriceSource;
// Services
private TickerService tickerService;
private JupiterPerpsAlarm jupiterPerpsAlarm;
private Evelyn evelynServiceProd;
private Evelyn evelynServiceTest;
private JupiterSwapService jupiterSwapService;
private BoundNotificationService discordAssetAZTokenNotificationService;
private EvelynIOUBurnerService evelynIOUBurnerServiceProd;
// Application components
private EvelynMissionControl missionControl;
private final NenjimRegistryServiceAdmin registryAdmin;
}