diff --git a/pom.xml b/pom.xml index a5210f386..ae21e66e2 100644 --- a/pom.xml +++ b/pom.xml @@ -10,18 +10,18 @@ 3.8.1 true - 17 - 17 UTF-8 UTF-8 + 3.9.4 quarkus-universe-bom io.quarkus 3.9.4 - 3.2.5 - 3.0.0-M5 7.4.0 2.5.1 + + 3.2.5 + 3.5.0 @@ -203,6 +203,16 @@ Saxon-HE 10.3 + + de.servicehealth.libcetp + lib-cetp + 1.0-SNAPSHOT + + + org.mapstruct + mapstruct + 1.6.2 + health.ere api-telematik-service @@ -387,10 +397,6 @@ - - maven-compiler-plugin - ${compiler-plugin.version} - maven-surefire-plugin ${surefire-plugin.version} @@ -400,10 +406,25 @@ dev org.jboss.logmanager.LogManager INFO - ${maven.home} + + org.apache.maven.plugins + maven-compiler-plugin + ${compiler-plugin.version} + + 17 + 17 + + + org.mapstruct + mapstruct-processor + 1.6.2 + + + + com.github.spotbugs @@ -461,8 +482,9 @@ + org.apache.maven.plugins maven-failsafe-plugin - ${surefire-plugin.version} + ${failsafe-plugin.version} @@ -472,12 +494,8 @@ dev - - ${project.build.directory}/${project.build.finalName}-runner - - org.jboss.logmanager.LogManager - - ${maven.home} + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager diff --git a/src/main/java/health/ere/ps/config/AppConfig.java b/src/main/java/health/ere/ps/config/AppConfig.java index cd892d1fd..7bfad8905 100644 --- a/src/main/java/health/ere/ps/config/AppConfig.java +++ b/src/main/java/health/ere/ps/config/AppConfig.java @@ -1,6 +1,7 @@ package health.ere.ps.config; -import health.ere.ps.service.cetp.CETPServer; +import de.health.service.cetp.CETPServer; +import de.health.service.cetp.config.SubscriptionConfig; import jakarta.enterprise.context.ApplicationScoped; import org.eclipse.microprofile.config.inject.ConfigProperty; @@ -13,8 +14,9 @@ import java.util.logging.Level; import java.util.logging.Logger; +@SuppressWarnings({"LombokGetterMayBeUsed", "LombokSetterMayBeUsed"}) @ApplicationScoped -public class AppConfig { +public class AppConfig implements SubscriptionConfig { private static final Logger log = Logger.getLogger(AppConfig.class.getName()); @@ -194,8 +196,8 @@ public String getKonnectorHost() { return konnectorHost; } - public Integer getCetpPort() { - return cetpPort.orElse(CETPServer.PORT); + public int getCetpPort() { + return cetpPort.orElse(CETPServer.DEFAULT_PORT); } public String getConnectorCrypt() { diff --git a/src/main/java/health/ere/ps/config/RuntimeConfig.java b/src/main/java/health/ere/ps/config/RuntimeConfig.java index 499503569..4593f08ae 100644 --- a/src/main/java/health/ere/ps/config/RuntimeConfig.java +++ b/src/main/java/health/ere/ps/config/RuntimeConfig.java @@ -7,6 +7,8 @@ import java.util.logging.Level; import java.util.logging.Logger; +import de.health.service.cetp.config.IRuntimeConfig; +import de.health.service.cetp.config.IUserConfigurations; import health.ere.ps.model.config.UserConfigurations; import jakarta.enterprise.inject.Alternative; import jakarta.enterprise.inject.spi.CDI; @@ -14,7 +16,7 @@ import jakarta.servlet.http.HttpServletRequest; @Alternative -public class RuntimeConfig extends UserConfig { +public class RuntimeConfig extends UserConfig implements IRuntimeConfig { private static Logger log = Logger.getLogger(RuntimeConfig.class.getName()); @@ -39,7 +41,7 @@ public RuntimeConfig() { } } - public RuntimeConfig(UserConfigurations userConfigurations) { + public RuntimeConfig(IUserConfigurations userConfigurations) { this(); this.updateProperties(userConfigurations); } diff --git a/src/main/java/health/ere/ps/config/SimpleUserConfig.java b/src/main/java/health/ere/ps/config/SimpleUserConfig.java index 022da16bf..e7037607e 100644 --- a/src/main/java/health/ere/ps/config/SimpleUserConfig.java +++ b/src/main/java/health/ere/ps/config/SimpleUserConfig.java @@ -1,5 +1,9 @@ package health.ere.ps.config; +import de.health.service.cetp.config.IRuntimeConfig; +import de.health.service.cetp.config.IUserConfigurations; +import de.health.service.cetp.config.UserRuntimeConfig; + import java.util.Objects; public class SimpleUserConfig { @@ -52,7 +56,7 @@ public class SimpleUserConfig { - public SimpleUserConfig(UserConfig userConfig) { + public SimpleUserConfig(UserRuntimeConfig userConfig) { setValues(userConfig); } @@ -210,25 +214,28 @@ public void setIdpClientId(String idpClientId) { this.idpClientId = idpClientId; } - private void setValues(UserConfig userConfig) { - this.erixaHotfolder = userConfig.getConfigurations().getErixaHotfolder(); - this.erixaDrugstoreEmail = userConfig.getConfigurations().getErixaDrugstoreEmail(); - this.erixaUserEmail = userConfig.getConfigurations().getErixaUserEmail(); - this.erixaApiKey = userConfig.getConfigurations().getErixaApiKey(); - this.muster16TemplateProfile = userConfig.getConfigurations().getMuster16TemplateProfile(); - this.connectorBaseURL = userConfig.getConfigurations().getConnectorBaseURL(); - this.mandantId = userConfig.getConfigurations().getMandantId(); - this.workplaceId = userConfig.getConfigurations().getWorkplaceId(); - this.clientSystemId = userConfig.getConfigurations().getClientSystemId(); - this.userId = userConfig.getConfigurations().getUserId(); - this.version = userConfig.getConfigurations().getVersion(); - this.tvMode = userConfig.getConfigurations().getTvMode(); - if(userConfig.getClass().getName().contains("RuntimeConfig")) { - this.eHBAHandle = ((RuntimeConfig)userConfig).getEHBAHandle(); - this.SMCBHandle = ((RuntimeConfig)userConfig).getSMCBHandle(); - this.sendPreview = ((RuntimeConfig)userConfig).isSendPreview(); - this.idpAuthRequestRedirectURL = ((RuntimeConfig)userConfig).getIdpAuthRequestRedirectURL(); - this.idpClientId = ((RuntimeConfig)userConfig).getIdpClientId(); + private void setValues(UserRuntimeConfig userConfig) { + IUserConfigurations configurations = userConfig.getUserConfigurations(); + this.erixaHotfolder = configurations.getErixaHotfolder(); + this.erixaDrugstoreEmail = configurations.getErixaDrugstoreEmail(); + this.erixaUserEmail = configurations.getErixaUserEmail(); + this.erixaApiKey = configurations.getErixaApiKey(); + this.muster16TemplateProfile = configurations.getMuster16TemplateProfile(); + this.connectorBaseURL = configurations.getConnectorBaseURL(); + this.mandantId = configurations.getMandantId(); + this.workplaceId = configurations.getWorkplaceId(); + this.clientSystemId = configurations.getClientSystemId(); + this.userId = configurations.getUserId(); + this.version = configurations.getVersion(); + this.tvMode = configurations.getTvMode(); + + IRuntimeConfig runtimeConfig = userConfig.getRuntimeConfig(); + if (runtimeConfig != null) { + this.eHBAHandle = runtimeConfig.getEHBAHandle(); + this.SMCBHandle = runtimeConfig.getSMCBHandle(); + this.sendPreview = runtimeConfig.isSendPreview(); + this.idpAuthRequestRedirectURL = runtimeConfig.getIdpAuthRequestRedirectURL(); + this.idpClientId = runtimeConfig.getIdpClientId(); } } diff --git a/src/main/java/health/ere/ps/config/UserConfig.java b/src/main/java/health/ere/ps/config/UserConfig.java index fc67d100c..a036606ac 100644 --- a/src/main/java/health/ere/ps/config/UserConfig.java +++ b/src/main/java/health/ere/ps/config/UserConfig.java @@ -1,5 +1,9 @@ package health.ere.ps.config; +import de.health.service.cetp.config.IRuntimeConfig; +import de.health.service.cetp.config.IUserConfigurations; +import de.health.service.cetp.config.UserRuntimeConfig; +import de.health.service.cetp.konnektorconfig.KCUserConfigurations; import health.ere.ps.event.config.UserConfigurationsUpdateEvent; import health.ere.ps.model.config.UserConfigurations; import health.ere.ps.service.config.UserConfigurationService; @@ -13,7 +17,7 @@ import java.util.Optional; @ApplicationScoped -public class UserConfig { +public class UserConfig implements UserRuntimeConfig { @Inject UserConfigurationService configurationManagementService; @@ -55,6 +59,27 @@ void init() { public UserConfig() { } + @Override + public IUserConfigurations getUserConfigurations() { + return configurations; + } + + @Override + public IRuntimeConfig getRuntimeConfig() { + if (this instanceof RuntimeConfig runtimeConfig) { + return runtimeConfig; + } else { + return null; + } + } + + @Override + public UserRuntimeConfig copy() { + RuntimeConfig runtimeConfig = new RuntimeConfig(); + runtimeConfig.copyValuesFromUserConfig(this); + return runtimeConfig; + } + public UserConfigurations getConfigurations() { return configurations == null ? new UserConfigurations() : configurations; } @@ -119,8 +144,12 @@ public void handleUpdateProperties(@ObservesAsync UserConfigurationsUpdateEvent updateProperties(event.getConfigurations()); } - public void updateProperties(UserConfigurations configurations) { - this.configurations = configurations; + public void updateProperties(IUserConfigurations configurations) { + if (configurations instanceof UserConfigurations) { + this.configurations = (UserConfigurations) configurations; + } else if (configurations instanceof KCUserConfigurations kcUserConfigurations) { + this.configurations = new UserConfigurations(kcUserConfigurations.properties()); + } } private void updateProperties() { diff --git a/src/main/java/health/ere/ps/jmx/StatusMXBeanImpl.java b/src/main/java/health/ere/ps/jmx/StatusMXBeanImpl.java index 52241ad0c..9233fe01c 100644 --- a/src/main/java/health/ere/ps/jmx/StatusMXBeanImpl.java +++ b/src/main/java/health/ere/ps/jmx/StatusMXBeanImpl.java @@ -3,9 +3,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import de.health.service.cetp.SubscriptionManager; import health.ere.ps.config.RuntimeConfig; import health.ere.ps.model.status.Status; -import health.ere.ps.service.cetp.SubscriptionManager; import health.ere.ps.service.status.StatusService; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/health/ere/ps/model/config/UserConfigurations.java b/src/main/java/health/ere/ps/model/config/UserConfigurations.java index b0b639937..f4bc82506 100644 --- a/src/main/java/health/ere/ps/model/config/UserConfigurations.java +++ b/src/main/java/health/ere/ps/model/config/UserConfigurations.java @@ -1,5 +1,11 @@ package health.ere.ps.model.config; +import de.health.service.cetp.config.IUserConfigurations; +import jakarta.json.JsonObject; +import jakarta.json.bind.annotation.JsonbNillable; +import jakarta.json.bind.annotation.JsonbProperty; +import jakarta.servlet.http.HttpServletRequest; + import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; @@ -15,12 +21,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -import jakarta.json.JsonObject; -import jakarta.json.bind.annotation.JsonbNillable; -import jakarta.json.bind.annotation.JsonbProperty; -import jakarta.servlet.http.HttpServletRequest; - -public class UserConfigurations { +public class UserConfigurations implements IUserConfigurations { private static final Logger log = Logger.getLogger(UserConfigurations.class.getName()); diff --git a/src/main/java/health/ere/ps/resource/gematik/PharmacyResource.java b/src/main/java/health/ere/ps/resource/gematik/PharmacyResource.java index 19de186e5..86e1e8540 100644 --- a/src/main/java/health/ere/ps/resource/gematik/PharmacyResource.java +++ b/src/main/java/health/ere/ps/resource/gematik/PharmacyResource.java @@ -1,9 +1,9 @@ package health.ere.ps.resource.gematik; import de.gematik.ws.conn.vsds.vsdservice.v5.FaultMessage; +import de.health.service.cetp.SubscriptionManager; import health.ere.ps.config.RuntimeConfig; import health.ere.ps.config.UserConfig; -import health.ere.ps.service.cetp.SubscriptionManager; import health.ere.ps.service.gematik.PharmacyService; import jakarta.inject.Inject; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/health/ere/ps/service/cardlink/AddJWTConfigurator.java b/src/main/java/health/ere/ps/service/cardlink/AddJWTConfigurator.java index 52d1f0a9d..82cdbc81c 100644 --- a/src/main/java/health/ere/ps/service/cardlink/AddJWTConfigurator.java +++ b/src/main/java/health/ere/ps/service/cardlink/AddJWTConfigurator.java @@ -3,8 +3,8 @@ import de.gematik.ws.conn.connectorcontext.v2.ContextType; import de.gematik.ws.conn.eventservice.wsdl.v7.EventServicePortType; import de.gematik.ws.conn.eventservice.wsdl.v7.FaultMessage; +import de.health.service.cetp.konnektorconfig.KonnektorConfig; import health.ere.ps.config.RuntimeConfig; -import health.ere.ps.service.cetp.config.KonnektorConfig; import health.ere.ps.service.connector.provider.MultiConnectorServicesProvider; import health.ere.ps.service.gematik.PharmacyService; import io.quarkus.arc.Unremovable; diff --git a/src/main/java/health/ere/ps/service/cetp/CETPServer.java b/src/main/java/health/ere/ps/service/cetp/CETPServer.java deleted file mode 100644 index 0273d3a5f..000000000 --- a/src/main/java/health/ere/ps/service/cetp/CETPServer.java +++ /dev/null @@ -1,184 +0,0 @@ -package health.ere.ps.service.cetp; - -import health.ere.ps.config.AppConfig; -import health.ere.ps.service.cardlink.CardlinkWebsocketClient; -import health.ere.ps.service.cetp.codec.CETPDecoder; -import health.ere.ps.service.cetp.config.KonnektorConfig; -import health.ere.ps.service.cetp.tracker.TrackerService; -import health.ere.ps.service.common.security.SecretsManagerService; -import health.ere.ps.service.common.security.SecretsManagerService.KeyStoreType; -import health.ere.ps.service.gematik.PharmacyService; -import health.ere.ps.service.health.check.CardlinkWebsocketCheck; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.handler.ssl.ClientAuth; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.util.concurrent.EventExecutorGroup; -import io.quarkus.runtime.ShutdownEvent; -import io.quarkus.runtime.StartupEvent; -import jakarta.annotation.Priority; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; -import jakarta.inject.Inject; - -import javax.net.ssl.KeyManagerFactory; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; - -@ApplicationScoped -public class CETPServer { - - public static final int PORT = 8585; - - private static final Logger log = Logger.getLogger(CETPServer.class.getName()); - - List bossGroups = new ArrayList<>(); - List workerGroups = new ArrayList<>(); - - private final Map startedOnPorts = new HashMap<>(); - - @Inject - AppConfig appConfig; - - @Inject - PharmacyService pharmacyService; - - @Inject - SecretsManagerService secretsManagerService; - - @Inject - SubscriptionManager subscriptionManager; - - @Inject - TrackerService trackerService; - - @Inject - CardlinkWebsocketCheck cardlinkWebsocketCheck; - - // Make sure subscription manager get's onStart first, before CETPServer at least! - void onStart(@Observes @Priority(5200) StartupEvent ev) { - run(); - } - - void onShutdown(@Observes ShutdownEvent ev) { - log.info("Shutdown CETP Server on port " + appConfig.getCetpPort()); - if (workerGroups != null) { - workerGroups.stream().filter(Objects::nonNull).forEach(EventExecutorGroup::shutdownGracefully); - } - if (bossGroups != null) { - bossGroups.stream().filter(Objects::nonNull).forEach(EventExecutorGroup::shutdownGracefully); - } - } - - public Map getStartedOnPorts() { - return startedOnPorts; - } - - public void run() { - for (KonnektorConfig config : subscriptionManager.getKonnektorConfigs(null)) { - log.info("Running CETP Server on port " + config.getCetpPort() + " for cardlink server: " + config.getCardlinkEndpoint()); - runServer(config); - } - } - - private void runServer(KonnektorConfig config) { - NioEventLoopGroup bossGroup = new NioEventLoopGroup(); // (1) - bossGroups.add(bossGroup); - NioEventLoopGroup workerGroup = new NioEventLoopGroup(); - workerGroups.add(workerGroup); - Integer port = config.getCetpPort(); - try { - ServerBootstrap b = new ServerBootstrap(); // (2) - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) // (3) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new ChannelInitializer() { // (4) - @Override - public void initChannel(SocketChannel ch) { - try { - SslContext sslContext = SslContextBuilder - .forServer(getKeyFactoryManager(config)) - .clientAuth(ClientAuth.NONE) - .build(); - - CardlinkWebsocketClient websocketClient = new CardlinkWebsocketClient( - config.getCardlinkEndpoint(), - cardlinkWebsocketCheck - ); - ch.pipeline() - .addLast("ssl", sslContext.newHandler(ch.alloc())) - .addLast("logging", new LoggingHandler(LogLevel.DEBUG)) - .addLast(new LengthFieldBasedFrameDecoder(65536, 4, 4, 0, 0)) - .addLast(new CETPDecoder(config.getUserConfigurations())) - .addLast(new CETPServerHandler( - trackerService, - pharmacyService, - websocketClient) - ); - } catch (Exception e) { - log.log(Level.WARNING, "Failed to create SSL context", e); - } - } - }) - .option(ChannelOption.SO_BACKLOG, 128) // (5) - .childOption(ChannelOption.SO_KEEPALIVE, true); // (6) - - // Bind and start to accept incoming connections. - - ChannelFuture f = b.bind(port).sync(); // (7) - startedOnPorts.put(port.toString(), "STARTED"); - - // Wait until the server socket is closed. - // In this example, this does not happen, but you can do that to gracefully - // shut down your server. - f.channel().closeFuture(); //.sync(); - } catch (InterruptedException e) { - startedOnPorts.put(port.toString(), String.format("FAILED: %s", e.getMessage())); - log.log(Level.WARNING, "CETP Server interrupted", e); - } - } - - - private KeyManagerFactory getKeyFactoryManager(KonnektorConfig config) { - if (config.getUserConfigurations().getClientCertificate() == null) { - return secretsManagerService.getKeyManagerFactory(); - } else { - String connectorTlsCertAuthStorePwd = config.getUserConfigurations().getClientCertificatePassword(); - byte[] clientCertificateBytes = SecretsManagerService.getClientCertificateBytes(config.getUserConfigurations()); - try (ByteArrayInputStream certificateInputStream = new ByteArrayInputStream(clientCertificateBytes)) { - KeyStore ks = KeyStore.getInstance(KeyStoreType.PKCS12.getKeyStoreType()); - ks.load(certificateInputStream, connectorTlsCertAuthStorePwd.toCharArray()); - - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - keyManagerFactory.init(ks, connectorTlsCertAuthStorePwd.toCharArray()); - return keyManagerFactory; - } catch (UnrecoverableKeyException | KeyStoreException | NoSuchAlgorithmException | CertificateException | - IOException e) { - log.log(Level.SEVERE, "Could not create keyManagerFactory", e); - } - } - return null; - } -} diff --git a/src/main/java/health/ere/ps/service/cetp/CETPServerHandler.java b/src/main/java/health/ere/ps/service/cetp/CETPServerHandler.java index f0b0e0053..9bbcdea81 100644 --- a/src/main/java/health/ere/ps/service/cetp/CETPServerHandler.java +++ b/src/main/java/health/ere/ps/service/cetp/CETPServerHandler.java @@ -3,8 +3,8 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.IParser; import de.gematik.ws.conn.eventservice.v7.Event; +import de.health.service.cetp.config.IUserConfigurations; import health.ere.ps.config.RuntimeConfig; -import health.ere.ps.model.config.UserConfigurations; import health.ere.ps.service.cardlink.CardlinkWebsocketClient; import health.ere.ps.service.cetp.tracker.TrackerService; import health.ere.ps.service.gematik.PharmacyService; @@ -24,7 +24,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; -import static health.ere.ps.utils.Utils.printException; +import static de.health.service.cetp.utils.Utils.printException; public class CETPServerHandler extends ChannelInboundHandlerAdapter { @@ -62,7 +62,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { cardlinkWebsocketClient.connect(); @SuppressWarnings("unchecked") - Pair input = (Pair) msg; + Pair input = (Pair) msg; Event event = input.getKey(); if (event.getTopic().equals("CARD/INSERTED")) { @@ -89,7 +89,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) { log.fine(String.format("[%s] Card inserted: params: %s", correlationId, paramsStr)); try { - UserConfigurations uc = input.getValue(); + IUserConfigurations uc = input.getValue(); RuntimeConfig runtimeConfig = new RuntimeConfig(uc); Pair pair = pharmacyService.getEPrescriptionsForCardHandle( correlationId, cardHandle, null, runtimeConfig diff --git a/src/main/java/health/ere/ps/service/cetp/CETPServerHandlerFactory.java b/src/main/java/health/ere/ps/service/cetp/CETPServerHandlerFactory.java new file mode 100644 index 000000000..6a24dc6f7 --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/CETPServerHandlerFactory.java @@ -0,0 +1,43 @@ +package health.ere.ps.service.cetp; + +import de.health.service.cetp.CETPEventHandlerFactory; +import de.health.service.cetp.konnektorconfig.KonnektorConfig; +import health.ere.ps.service.cardlink.CardlinkWebsocketClient; +import health.ere.ps.service.cetp.tracker.TrackerService; +import health.ere.ps.service.common.security.SecretsManagerService; +import health.ere.ps.service.gematik.PharmacyService; +import health.ere.ps.service.health.check.CardlinkWebsocketCheck; +import io.netty.channel.ChannelInboundHandler; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class CETPServerHandlerFactory implements CETPEventHandlerFactory { + + TrackerService trackerService; + PharmacyService pharmacyService; + SecretsManagerService secretsManagerService; + CardlinkWebsocketCheck cardlinkWebsocketCheck; + + @Inject + public CETPServerHandlerFactory( + TrackerService trackerService, + PharmacyService pharmacyService, + SecretsManagerService secretsManagerService, + CardlinkWebsocketCheck cardlinkWebsocketCheck + ) { + this.trackerService = trackerService; + this.pharmacyService = pharmacyService; + this.secretsManagerService = secretsManagerService; + this.cardlinkWebsocketCheck = cardlinkWebsocketCheck; + } + + @Override + public ChannelInboundHandler build(KonnektorConfig konnektorConfig) { + CardlinkWebsocketClient cardlinkWebsocketClient = new CardlinkWebsocketClient( + konnektorConfig.getCardlinkEndpoint(), + cardlinkWebsocketCheck + ); + return new CETPServerHandler(trackerService, pharmacyService, cardlinkWebsocketClient); + } +} diff --git a/src/main/java/health/ere/ps/service/cetp/KonnektorClient.java b/src/main/java/health/ere/ps/service/cetp/KonnektorClient.java index a61a3dfed..ef09f6073 100644 --- a/src/main/java/health/ere/ps/service/cetp/KonnektorClient.java +++ b/src/main/java/health/ere/ps/service/cetp/KonnektorClient.java @@ -5,56 +5,83 @@ import de.gematik.ws.conn.eventservice.v7.GetSubscription; import de.gematik.ws.conn.eventservice.v7.GetSubscriptionResponse; import de.gematik.ws.conn.eventservice.v7.RenewSubscriptionsResponse; +import de.gematik.ws.conn.eventservice.v7.SubscriptionRenewal; import de.gematik.ws.conn.eventservice.v7.SubscriptionType; import de.gematik.ws.conn.eventservice.wsdl.v7.EventServicePortType; import de.gematik.ws.conn.eventservice.wsdl.v7.FaultMessage; -import health.ere.ps.config.RuntimeConfig; +import de.health.service.cetp.IKonnektorClient; +import de.health.service.cetp.config.UserRuntimeConfig; +import de.health.service.cetp.domain.CetpStatus; +import de.health.service.cetp.domain.SubscriptionResult; +import de.health.service.cetp.domain.eventservice.Subscription; +import de.health.service.cetp.domain.fault.CetpFault; +import health.ere.ps.service.cetp.mapper.StatusMapper; +import health.ere.ps.service.cetp.mapper.SubscriptionMapper; +import health.ere.ps.service.cetp.mapper.SubscriptionResultMapper; import health.ere.ps.service.connector.provider.MultiConnectorServicesProvider; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.xml.ws.Holder; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.lang3.tuple.Triple; import javax.xml.datatype.XMLGregorianCalendar; import java.util.List; +import java.util.stream.Collectors; -import static health.ere.ps.service.cetp.SubscriptionManager.FAILED; +import static de.health.service.cetp.SubscriptionManager.FAILED; @ApplicationScoped -public class KonnektorClient { +public class KonnektorClient implements IKonnektorClient { - public static final String CARD_INSERTED_TOPIC = "CARD/INSERTED"; + private final Object emptyInput = new Object(); @Inject MultiConnectorServicesProvider connectorServicesProvider; - public List getSubscriptions(RuntimeConfig runtimeConfig) throws FaultMessage { + @Inject + SubscriptionResultMapper subscriptionResultMapper; + + @Inject + SubscriptionMapper subscriptionMapper; + + @Inject + StatusMapper statusMapper; + + @Override + public List getSubscriptions(UserRuntimeConfig runtimeConfig) throws CetpFault { ContextType context = connectorServicesProvider.getContextType(runtimeConfig); EventServicePortType eventService = connectorServicesProvider.getEventServicePortType(runtimeConfig); GetSubscription getSubscriptionRequest = new GetSubscription(); getSubscriptionRequest.setContext(context); getSubscriptionRequest.setMandantWide(false); - GetSubscriptionResponse subscriptionResponse = eventService.getSubscription(getSubscriptionRequest); - return subscriptionResponse.getSubscriptions().getSubscription(); + try { + GetSubscriptionResponse subscriptionResponse = eventService.getSubscription(getSubscriptionRequest); + return subscriptionResponse.getSubscriptions().getSubscription() + .stream().map(subscriptionMapper::toDomain) + .collect(Collectors.toList()); + } catch (FaultMessage faultMessage) { + throw new CetpFault(faultMessage.getMessage()); + } } - public Pair renewSubscription(RuntimeConfig runtimeConfig, String subscriptionId) throws FaultMessage { + @Override + public SubscriptionResult renewSubscription(UserRuntimeConfig runtimeConfig, String subscriptionId) throws CetpFault { ContextType context = connectorServicesProvider.getContextType(runtimeConfig); EventServicePortType eventService = connectorServicesProvider.getEventServicePortType(runtimeConfig); Holder statusHolder = new Holder<>(); Holder renewalHolder = new Holder<>(); List subscriptions = List.of(subscriptionId); - - eventService.renewSubscriptions(context, subscriptions, statusHolder, renewalHolder); - return Pair.of(statusHolder.value, renewalHolder.value.getSubscriptionRenewal().get(0).getSubscriptionID()); + try { + eventService.renewSubscriptions(context, subscriptions, statusHolder, renewalHolder); + SubscriptionRenewal renewal = renewalHolder.value.getSubscriptionRenewal().get(0); + return subscriptionResultMapper.toDomain(renewal, statusHolder); + } catch (FaultMessage faultMessage) { + throw new CetpFault(faultMessage.getMessage()); + } } - public Triple subscribeToKonnektor( - RuntimeConfig runtimeConfig, - String cetpHost - ) throws FaultMessage { + @Override + public SubscriptionResult subscribe(UserRuntimeConfig runtimeConfig, String cetpHost) throws CetpFault { ContextType context = connectorServicesProvider.getContextType(runtimeConfig); EventServicePortType eventService = connectorServicesProvider.getEventServicePortType(runtimeConfig); @@ -62,32 +89,39 @@ public Triple subscribeToKonnektor( subscriptionType.setEventTo(cetpHost); subscriptionType.setTopic(CARD_INSERTED_TOPIC); - Holder status = new Holder<>(); + Holder statusHolder = new Holder<>(); Holder subscriptionId = new Holder<>(); Holder terminationTime = new Holder<>(); - - eventService.subscribe(context, subscriptionType, status, subscriptionId, terminationTime); - - return Triple.of(status.value, subscriptionId.value, terminationTime.value.toString()); + try { + eventService.subscribe(context, subscriptionType, statusHolder, subscriptionId, terminationTime); + return subscriptionResultMapper.toDomain(emptyInput, statusHolder, subscriptionId, terminationTime); + } catch (FaultMessage faultMessage) { + throw new CetpFault(faultMessage.getMessage()); + } } - public Status unsubscribeFromKonnektor( - RuntimeConfig runtimeConfig, + @Override + public CetpStatus unsubscribe( + UserRuntimeConfig runtimeConfig, String subscriptionId, String cetpHost, boolean forceCetp - ) throws FaultMessage { + ) throws CetpFault { ContextType context = connectorServicesProvider.getContextType(runtimeConfig); EventServicePortType eventService = connectorServicesProvider.getEventServicePortType(runtimeConfig); - if (forceCetp) { - return eventService.unsubscribe(context, null, cetpHost); - } else { - if (subscriptionId == null || subscriptionId.startsWith(FAILED)) { - Status status = new Status(); - status.setResult("Previous subscription is not found"); - return status; + try { + if (forceCetp) { + return statusMapper.toDomain(eventService.unsubscribe(context, null, cetpHost)); + } else { + if (subscriptionId == null || subscriptionId.startsWith(FAILED)) { + CetpStatus status = new CetpStatus(); + status.setResult("Previous subscription is not found"); + return status; + } + return statusMapper.toDomain(eventService.unsubscribe(context, subscriptionId, null)); } - return eventService.unsubscribe(context, subscriptionId, null); + } catch (FaultMessage faultMessage) { + throw new CetpFault(faultMessage.getMessage()); } } } diff --git a/src/main/java/health/ere/ps/service/cetp/LocalAddressInSameSubnetFinder.java b/src/main/java/health/ere/ps/service/cetp/LocalAddressInSameSubnetFinder.java deleted file mode 100644 index 3ad356c22..000000000 --- a/src/main/java/health/ere/ps/service/cetp/LocalAddressInSameSubnetFinder.java +++ /dev/null @@ -1,122 +0,0 @@ -package health.ere.ps.service.cetp; - - -import com.google.common.annotations.VisibleForTesting; - -import java.net.*; -import java.util.Collections; -import java.util.List; - -/** - * Most computers nowadays have multiple (possibly virtual) network interfaces attached and hence possibly listen - * to multiple ip addresses. - * - * Given that we know some external IP address, we can check out our own network interfaces if we have one - * ip address being in the same subnet as the external ip. - * - * This is especially useful in a scenario where we receive a broadcast UDP packet: Broadcasts can only be received - * if one is in the same subnet as the broadcast sender. Hence, if we know the broadcaster ip, we can pretty - * confidentally predict the network card (and IP address our lur local host) that received the broadcast to e.g. - * respond to the broadcaster our own IP they can reach us (e.g. Gematik Konnektor). - */ -public class LocalAddressInSameSubnetFinder { - - public static Inet4Address findLocalIPinSameSubnet(Inet4Address peer) throws SocketException { - return findLocalIPinSameSubnet(Collections.list(NetworkInterface.getNetworkInterfaces()), peer); - } - - public static Inet4Address findLocalIPinSameSubnet(List nics, Inet4Address peer) { - return new LocalAddressInSameSubnetFinder().findHostAddress(nics, peer); - } - - @VisibleForTesting - LocalAddressInSameSubnetFinder() { - // NOOP - } - - @VisibleForTesting - Inet4Address findHostAddress(List interfaces, Inet4Address peer) { - if (peer == null) { - return null; - } - Inet4Address bestMatch = null; - int bestMatchPrefix = -1; - for (NetworkInterface ni : interfaces) { - if (isLoopBack(ni) || !isUp(ni)) { - continue; - } - - for (InterfaceAddress ia : ni.getInterfaceAddresses()) { - InetAddress address = ia.getAddress(); - // Only support IPv4 address in this entire class currently. - if ((address instanceof Inet4Address ip4address) && (ia.getNetworkPrefixLength() > bestMatchPrefix) && isInSubnet(ia, peer)) { - bestMatch = ip4address; - bestMatchPrefix = ia.getNetworkPrefixLength(); - } - } - } - - if (bestMatch != null) { - return bestMatch; - } - - return null; - } - - @VisibleForTesting - boolean isInSubnet(InterfaceAddress ifa, InetAddress ipAddress) { - InetAddress networkAddress = ifa.getAddress(); - int networkPrefixLength = ifa.getNetworkPrefixLength(); - - byte[] networkAddressBytes = networkAddress.getAddress(); - byte[] ipAddressBytes = ipAddress.getAddress(); - - if (networkAddressBytes.length != ipAddressBytes.length) { - // IPv4 and IPv6 length mismatch - return false; - } - - int byteCount = networkAddressBytes.length; - int bitCount = networkPrefixLength; - - for (int i = 0; i < byteCount; i++) { - int networkByte = networkAddressBytes[i] & 0xFF; - int ipByte = ipAddressBytes[i] & 0xFF; - - // Calculate the mask for the current byte - int mask = (bitCount >= 8) ? 0xFF : (0xFF << (8 - bitCount)); - - // Apply the mask and compare - if ((networkByte & mask) != (ipByte & mask)) { - return false; - } - - // Subtract the number of bits we just processed - bitCount -= 8; - if (bitCount <= 0) { - break; - } - } - - return true; - } - - - private boolean isLoopBack(NetworkInterface nic) { - try { - return nic.isLoopback(); - } catch (SocketException e) { - // Wrap to RTE, should not happen... - throw new RuntimeException(e); - } - } - - private boolean isUp(NetworkInterface nic) { - try { - return nic.isUp(); - } catch (SocketException e) { - // Wrap to RTE, should not happen... - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/health/ere/ps/service/cetp/RegisterSMCBJob.java b/src/main/java/health/ere/ps/service/cetp/RegisterSMCBJob.java index 99b7af98c..7feb9255c 100644 --- a/src/main/java/health/ere/ps/service/cetp/RegisterSMCBJob.java +++ b/src/main/java/health/ere/ps/service/cetp/RegisterSMCBJob.java @@ -9,9 +9,12 @@ import java.util.logging.Level; import java.util.logging.Logger; +import de.health.service.cetp.SubscriptionManager; +import de.health.service.cetp.konnektorconfig.KonnektorConfig; +import health.ere.ps.jmx.SubscriptionsMXBean; +import health.ere.ps.jmx.SubscriptionsMXBeanImpl; import health.ere.ps.service.cardlink.AddJWTConfigurator; import health.ere.ps.service.cardlink.CardlinkWebsocketClient; -import health.ere.ps.service.cetp.config.KonnektorConfig; import health.ere.ps.service.health.check.CardlinkWebsocketCheck; import io.quarkus.runtime.StartupEvent; import io.quarkus.scheduler.Scheduled; @@ -20,6 +23,8 @@ import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; +import static health.ere.ps.jmx.PsMXBeanManager.registerMXBean; + @ApplicationScoped public class RegisterSMCBJob { @@ -41,6 +46,10 @@ void onStart(@Observes @Priority(5300) StartupEvent ev) { private void initWSClients() { Collection konnektorConfigs = subscriptionManager.getKonnektorConfigs(null); + + SubscriptionsMXBeanImpl subscriptionsMXBean = new SubscriptionsMXBeanImpl(konnektorConfigs.size()); + registerMXBean(SubscriptionsMXBean.OBJECT_NAME, subscriptionsMXBean); + cardlinkWebsocketClients = new ArrayList<>(); konnektorConfigs.forEach(kc -> cardlinkWebsocketClients.add(new CardlinkWebsocketClient( diff --git a/src/main/java/health/ere/ps/service/cetp/SubscriptionManager.java b/src/main/java/health/ere/ps/service/cetp/SubscriptionManager.java deleted file mode 100644 index 7eaf0d642..000000000 --- a/src/main/java/health/ere/ps/service/cetp/SubscriptionManager.java +++ /dev/null @@ -1,391 +0,0 @@ -package health.ere.ps.service.cetp; - -import de.gematik.ws.conn.connectorcommon.v5.Status; -import de.gematik.ws.conn.eventservice.v7.SubscriptionType; -import de.gematik.ws.conn.eventservice.wsdl.v7.FaultMessage; -import de.gematik.ws.tel.error.v2.Error; -import health.ere.ps.config.AppConfig; -import health.ere.ps.config.RuntimeConfig; -import health.ere.ps.jmx.PsMXBeanManager; -import health.ere.ps.jmx.SubscriptionsMXBean; -import health.ere.ps.jmx.SubscriptionsMXBeanImpl; -import health.ere.ps.retry.Retrier; -import health.ere.ps.service.cetp.config.KonnektorConfig; -import health.ere.ps.service.cetp.config.KonnektorConfigService; -import io.quarkus.runtime.StartupEvent; -import io.quarkus.scheduler.Scheduled; -import jakarta.annotation.Priority; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; -import jakarta.inject.Inject; -import jakarta.xml.ws.Holder; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.lang3.tuple.Triple; - -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.time.OffsetDateTime; -import java.util.Collection; -import java.util.Comparator; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import static health.ere.ps.utils.Utils.printException; - -@ApplicationScoped -public class SubscriptionManager { - - private final static Logger log = Logger.getLogger(SubscriptionManager.class.getName()); - - public static final String FAILED = "failed"; - - private final Map hostToKonnektorConfig = new ConcurrentHashMap<>(); - - AppConfig appConfig; - KonnektorClient konnektorClient; - KonnektorConfigService kcService; - - private ExecutorService threadPool; - - @Inject - public SubscriptionManager( - AppConfig appConfig, - KonnektorClient konnektorClient, - KonnektorConfigService kcService - ) { - this.appConfig = appConfig; - this.konnektorClient = konnektorClient; - this.kcService = kcService; - } - - // Make sure subscription manager get's onStart first, before CETPServer at least! - public void onStart(@Observes @Priority(5100) StartupEvent ev) { - hostToKonnektorConfig.putAll(kcService.loadConfigs()); - threadPool = Executors.newFixedThreadPool(hostToKonnektorConfig.size()); - SubscriptionsMXBeanImpl subscriptionsMXBean = new SubscriptionsMXBeanImpl(hostToKonnektorConfig.size()); - PsMXBeanManager.registerMXBean(SubscriptionsMXBean.OBJECT_NAME, subscriptionsMXBean); - } - - @Scheduled( - every = "${cetp.subscription.maintenance.interval.sec:3s}", - delay = 5, - delayUnit = TimeUnit.SECONDS, - concurrentExecution = Scheduled.ConcurrentExecution.SKIP - ) - void subscriptionsMaintenance() { - String defaultSender = appConfig.getEventToHost().orElse(null); - if (defaultSender == null) { - log.log(Level.WARNING, "You did not set 'cetp.subscriptions.event-to-host' property. Will have no fallback if Konnektor is not found to be in the same subnet"); - } - List retryMillis = List.of(200); - int intervalMs = appConfig.getSubscriptionsMaintenanceRetryIntervalMs(); - List> futures = getKonnektorConfigs(null).stream().map(kc -> threadPool.submit(() -> { - Inet4Address meInSameSubnet = LocalAddressInSameSubnetFinder.findLocalIPinSameSubnet(konnektorToIp4(kc.getHost())); - String eventToHost = (meInSameSubnet != null) ? meInSameSubnet.getHostAddress() : defaultSender; - if (eventToHost == null) { - log.log(Level.INFO, "Can't maintain subscription. Don't know my own address to tell konnektor about it"); - return false; - } - Boolean result = Retrier.callAndRetry( - retryMillis, - intervalMs, - () -> renewSubscriptions(eventToHost, kc), - bool -> bool - ); - if (!result) { - String msg = String.format( - "[%s] Subscriptions maintenance is failed within %d ms retry", kc.getHost(), intervalMs); - log.warning(msg); - } - return result; - })).toList(); - for (Future future : futures) { - try { - future.get(); - } catch (Throwable e) { - log.log(Level.SEVERE, "Subscriptions maintenance error", e); - } - } - } - - public boolean renewSubscriptions(String eventToHost, KonnektorConfig kc) { - Semaphore semaphore = kc.getSemaphore(); - if (semaphore.tryAcquire()) { - try { - RuntimeConfig runtimeConfig = modifyRuntimeConfig(null, kc); - String cetpHost = "cetp://" + eventToHost + ":" + kc.getCetpPort(); - List subscriptions = konnektorClient.getSubscriptions(runtimeConfig) - .stream().filter(st -> st.getEventTo().contains(eventToHost)).toList(); - - Holder resultHolder = new Holder<>(); - if (subscriptions.isEmpty()) { - return subscribe(kc, runtimeConfig, cetpHost, resultHolder); - } else { - Optional newestOpt = subscriptions.stream().max( - Comparator.comparing(o -> o.getTerminationTime().toGregorianCalendar().getTime()) - ); - SubscriptionType newest = newestOpt.get(); - Date expireDate = newest.getTerminationTime().toGregorianCalendar().getTime(); - Date now = new Date(); - boolean expired = now.getTime() >= expireDate.getTime(); - - // force re-subscribe every 12 hours - int periodSeconds = appConfig.getForceResubscribePeriodSeconds(); - boolean forceSubscribe = kc.getSubscriptionTime().plusSeconds(periodSeconds).isBefore(OffsetDateTime.now()); - if (expired || forceSubscribe) { - boolean subscribed = subscribe(kc, runtimeConfig, cetpHost, resultHolder); - if (forceSubscribe && subscribed) { - log.info(String.format("Force subscribed to %s: %s", kc.getHost(), resultHolder.value)); - } - // all are expired, drop them - boolean dropped = drop(runtimeConfig, subscriptions).isEmpty(); - return subscribed && dropped; - } else { - boolean renewed = renew(runtimeConfig, kc, newest, now, expireDate); - List olderSubscriptions = subscriptions.stream() - .filter(st -> !st.getSubscriptionID().equals(newest.getSubscriptionID())) - .toList(); - boolean dropped = drop(runtimeConfig, olderSubscriptions).isEmpty(); - return renewed && dropped; - } - } - } catch (FaultMessage fm) { - log.log(Level.SEVERE, String.format("[%s] Subscriptions maintenance error", kc.getHost()), fm); - return false; - } finally { - semaphore.release(); - } - } else { - log.warning(String.format("[%s] Subscription maintenance is in progress, try later", kc.getHost())); - return true; - } - } - - public boolean renew( - RuntimeConfig runtimeConfig, - KonnektorConfig konnektorConfig, - SubscriptionType type, - Date now, - Date expireDate - ) throws FaultMessage { - String newestSubscriptionId = type.getSubscriptionID(); - boolean sameSubscription = newestSubscriptionId.equals(konnektorConfig.getSubscriptionId()); - if (!sameSubscription) { - String msg = String.format( - "Found subscriptions discrepancy: CONFIG=%s, REAL=%s, REAL_EXPIRATION=%s, updating config", - konnektorConfig.getSubscriptionId(), - newestSubscriptionId, - expireDate - ); - log.warning(msg); - kcService.saveSubscription(konnektorConfig, newestSubscriptionId, null); - } - int safePeriod = appConfig.getCetpSubscriptionsRenewalSafePeriodMs(); - if (now.getTime() + safePeriod >= expireDate.getTime()) { - String msg = String.format( - "Subscription %s is about to expire after %d seconds, renew", newestSubscriptionId, safePeriod / 1000 - ); - log.info(msg); - Pair pair = konnektorClient.renewSubscription(runtimeConfig, newestSubscriptionId); - Error error = pair.getKey().getError(); - String renewedSubscriptionId = pair.getValue(); - if (error == null && !renewedSubscriptionId.equals(newestSubscriptionId)) { - msg = String.format( - "Subscription ID has changed after renew: OLD=%s, NEW=%s, updating config", - newestSubscriptionId, - renewedSubscriptionId - ); - log.fine(msg); - kcService.saveSubscription(konnektorConfig, renewedSubscriptionId, null); - } - return error == null; - } else { - return true; - } - } - - private String printError(Error error) { - return String.format( - "[%s] Gematik ERROR at %s: %s ", - error.getMessageID(), - error.getTimestamp(), - error.getTrace().stream().map(t -> - String.format("Code=%s ErrorText=%s Detail=%s", t.getCode(), t.getErrorText(), t.getDetail().getValue()) - ).collect(Collectors.joining(", ")) - ); - } - - public List drop( - RuntimeConfig runtimeConfig, - List subscriptions - ) throws FaultMessage { - return subscriptions.stream().map(s -> { - try { - Status status = konnektorClient.unsubscribeFromKonnektor(runtimeConfig, s.getSubscriptionID(), null, false); - Error error = status.getError(); - if (error == null) { - return Pair.of(true, s.getSubscriptionID()); - } else { - String msg = String.format("Failed to unsubscribe %s", s.getSubscriptionID()); - log.log(Level.SEVERE, msg, printError(error)); - return Pair.of(false, s.getSubscriptionID()); - } - } catch (FaultMessage f) { - String msg = String.format("Failed to unsubscribe %s", s.getSubscriptionID()); - log.log(Level.SEVERE, msg, f); - return Pair.of(false, s.getSubscriptionID()); - } - }).filter(p -> !p.getKey()).map(Pair::getValue).toList(); - } - - public Collection getKonnektorConfigs(String host) { - return host == null - ? hostToKonnektorConfig.values() - : hostToKonnektorConfig.entrySet().stream().filter(entry -> entry.getKey().contains(host)).map(Map.Entry::getValue).toList(); - } - - private RuntimeConfig modifyRuntimeConfig(RuntimeConfig runtimeConfig, KonnektorConfig konnektorConfig) { - if (runtimeConfig == null) { - return new RuntimeConfig(konnektorConfig.getUserConfigurations()); - } else { - runtimeConfig.updateProperties(konnektorConfig.getUserConfigurations()); - return runtimeConfig; - } - } - - public List manage( - RuntimeConfig runtimeConfig, - String host, - String eventToHost, - boolean forceCetp, - boolean subscribe - ) { - Collection konnektorConfigs = getKonnektorConfigs(host); - List statuses = konnektorConfigs.stream().map(kc -> { - Semaphore semaphore = kc.getSemaphore(); - if (semaphore.tryAcquire()) { - try { - String cetpHost = "cetp://" + eventToHost + ":" + kc.getCetpPort(); - return process(kc, modifyRuntimeConfig(runtimeConfig, kc), cetpHost, forceCetp, subscribe); - } finally { - semaphore.release(); - } - } else { - try { - String h = host == null ? kc.getHost() : host; - String s = subscribe ? "subscription" : "unsubscription"; - return String.format("[%s] Host %s is in progress, try later", h, s); - } catch (Exception e) { - return e.getMessage(); - } - } - }) - .filter(Objects::nonNull) - .toList(); - - if (statuses.isEmpty()) { - return List.of(String.format("No configuration is found for the given host: %s", host)); - } - return statuses; - } - - private boolean subscribe( - KonnektorConfig konnektorConfig, - RuntimeConfig runtimeConfig, - String cetpHost, - Holder resultHolder - ) throws FaultMessage { - Triple triple = konnektorClient.subscribeToKonnektor(runtimeConfig, cetpHost); - Status status = triple.getLeft(); - Error error = status.getError(); - if (error == null) { - String newSubscriptionId = triple.getMiddle(); - resultHolder.value = status.getResult() + " " + newSubscriptionId + " " + triple.getRight(); - kcService.saveSubscription(konnektorConfig, newSubscriptionId, null); - log.info(String.format("Subscribe status for subscriptionId=%s: %s", newSubscriptionId, status.getResult())); - return true; - } else { - String statusResult = printError(error); - resultHolder.value = statusResult; - String subscriptionId = konnektorConfig.getSubscriptionId(); - String fileName = subscriptionId != null && subscriptionId.startsWith(FAILED) - ? subscriptionId - : String.format("%s-%s", FAILED, subscriptionId); - - kcService.saveSubscription(konnektorConfig, fileName, statusResult); - log.log(Level.WARNING, String.format("Could not subscribe -> %s", statusResult)); - return false; - } - } - - private String process( - KonnektorConfig konnektorConfig, - RuntimeConfig runtimeConfig, - String cetpHost, - boolean forceCetp, - boolean subscribe - ) { - String subscriptionId = konnektorConfig.getSubscriptionId(); - String failedUnsubscriptionFileName = subscriptionId != null && subscriptionId.startsWith(FAILED) - ? subscriptionId - : String.format("%s-unsubscription-%s", FAILED, subscriptionId); - - String failedSubscriptionFileName = String.format("%s-subscription", FAILED); - - String statusResult; - boolean unsubscribed = false; - try { - Status status = konnektorClient.unsubscribeFromKonnektor(runtimeConfig, subscriptionId, cetpHost, forceCetp); - Error error = status.getError(); - if (error == null) { - unsubscribed = true; - statusResult = status.getResult(); - log.info(String.format("Unsubscribe status for subscriptionId=%s: %s", subscriptionId, statusResult)); - if (subscribe) { - Holder resultHolder = new Holder<>(); - subscribe(konnektorConfig, runtimeConfig, cetpHost, resultHolder); - statusResult = resultHolder.value; - } else { - kcService.cleanUp(konnektorConfig, null); - } - } else { - statusResult = printError(error); - kcService.saveSubscription(konnektorConfig, failedUnsubscriptionFileName, statusResult); - String msg = String.format("Could not unsubscribe from %s -> %s", subscriptionId, printError(error)); - log.log(Level.WARNING, msg); - } - } catch (Exception e) { - String fileName = unsubscribed ? failedSubscriptionFileName : failedUnsubscriptionFileName; - kcService.saveSubscription(konnektorConfig, fileName, printException(e)); - log.log(Level.WARNING, "Error: " + fileName, e); - statusResult = e.getMessage(); - } - return statusResult; - } - - private Inet4Address konnektorToIp4(String host) { - try { - InetAddress inetAddress = InetAddress.getByName(host); - if (inetAddress instanceof Inet4Address addr) { - return addr; - } else { - return null; - } - } catch (UnknownHostException e) { - return null; - } - } -} \ No newline at end of file diff --git a/src/main/java/health/ere/ps/service/cetp/codec/CETPDecoder.java b/src/main/java/health/ere/ps/service/cetp/codec/CETPDecoder.java index 53fd41ba5..f932759ca 100644 --- a/src/main/java/health/ere/ps/service/cetp/codec/CETPDecoder.java +++ b/src/main/java/health/ere/ps/service/cetp/codec/CETPDecoder.java @@ -6,6 +6,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import de.health.service.cetp.config.IUserConfigurations; import org.apache.commons.lang3.tuple.Pair; import de.gematik.ws.conn.eventservice.v7.Event; @@ -29,13 +30,13 @@ public class CETPDecoder extends ByteToMessageDecoder { log.log(Level.SEVERE, "Failed to create JAXB context", e); } } - UserConfigurations userConfigurations; + IUserConfigurations userConfigurations; public CETPDecoder() { } - public CETPDecoder(UserConfigurations userConfigurations) { + public CETPDecoder(IUserConfigurations userConfigurations) { this.userConfigurations = userConfigurations; } diff --git a/src/main/java/health/ere/ps/service/cetp/codec/CETPDecoderFactory.java b/src/main/java/health/ere/ps/service/cetp/codec/CETPDecoderFactory.java new file mode 100644 index 000000000..95adc51d8 --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/codec/CETPDecoderFactory.java @@ -0,0 +1,15 @@ +package health.ere.ps.service.cetp.codec; + +import de.health.service.cetp.codec.CETPEventDecoderFactory; +import de.health.service.cetp.config.IUserConfigurations; +import io.netty.channel.ChannelInboundHandler; +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class CETPDecoderFactory implements CETPEventDecoderFactory { + + @Override + public ChannelInboundHandler build(IUserConfigurations userConfigurations) { + return new CETPDecoder(userConfigurations); + } +} diff --git a/src/main/java/health/ere/ps/service/cetp/config/FSConfigService.java b/src/main/java/health/ere/ps/service/cetp/config/FSConfigService.java deleted file mode 100644 index 5db66ba69..000000000 --- a/src/main/java/health/ere/ps/service/cetp/config/FSConfigService.java +++ /dev/null @@ -1,159 +0,0 @@ -package health.ere.ps.service.cetp.config; - -import health.ere.ps.config.AppConfig; -import health.ere.ps.config.UserConfig; -import health.ere.ps.model.config.UserConfigurations; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; -import org.eclipse.microprofile.config.inject.ConfigProperty; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import static health.ere.ps.service.cetp.SubscriptionManager.FAILED; -import static health.ere.ps.utils.Utils.writeFile; - -@ApplicationScoped -public class FSConfigService implements KonnektorConfigService { - - private final static Logger log = Logger.getLogger(FSConfigService.class.getName()); - - public static final String PROPERTIES_EXT = ".properties"; - - @ConfigProperty(name = "ere.per.konnektor.config.folder") - String configFolder; - - @Inject - AppConfig appConfig; - - @Inject - UserConfig userConfig; - - - @Override - public Map loadConfigs() { - List configs = new ArrayList<>(); - var konnektorConfigFolder = new File(configFolder); - if (konnektorConfigFolder.exists()) { - configs = readFromPath(konnektorConfigFolder.getAbsolutePath()); - } - if (configs.isEmpty()) { - configs.add( - new KonnektorConfig( - konnektorConfigFolder, - appConfig.getCetpPort(), - userConfig.getConfigurations(), - appConfig.getCardLinkURI() - ) - ); - } - return configs.stream().collect(Collectors.toMap(this::getKonnectorKey, config -> config)); - } - - private String getKonnectorKey(KonnektorConfig config) { - String konnectorHost = config.getHost(); - String host = konnectorHost == null ? appConfig.getKonnectorHost() : konnectorHost; - return String.format("%d_%s", config.getCetpPort(), host); - } - - public List readFromPath(String path) { - File folderFile = new File(path); - if (folderFile.exists() && folderFile.isDirectory()) { - File[] files = folderFile.listFiles(); - if (files != null) { - return Arrays.stream(files) - .filter(File::isDirectory) - .map(this::fromFolder) - .filter(Objects::nonNull) - .sorted(Comparator.comparing(KonnektorConfig::getCetpPort)) - .collect(Collectors.toList()); - } - } - return List.of(); - } - - private KonnektorConfig fromFolder(File folder) { - Optional userPropertiesOpt = Arrays.stream(folder.listFiles()) - .filter(f -> f.getName().endsWith(PROPERTIES_EXT)) - .max(Comparator.comparingLong(File::lastModified)); - - Optional subscriptionFileOpt = Arrays.stream(folder.listFiles()) - .filter(f -> !f.getName().startsWith(FAILED) && !f.getName().endsWith(PROPERTIES_EXT)) - .max(Comparator.comparingLong(File::lastModified)); - - if (userPropertiesOpt.isPresent()) { - File actualSubscription = userPropertiesOpt.get(); - if (actualSubscription.exists()) { - String subscriptionId = subscriptionFileOpt.map(File::getName).orElse(null); - OffsetDateTime subscriptionTime = subscriptionFileOpt - .map(f -> OffsetDateTime.ofInstant(Instant.ofEpochMilli(f.lastModified()), ZoneId.systemDefault())) - .orElse(OffsetDateTime.now().minusDays(30)); // force subscribe if no subscription is found - try (var fis = new FileInputStream(actualSubscription)) { - Properties properties = new Properties(); - properties.load(fis); - KonnektorConfig konnektorConfig = new KonnektorConfig(); - konnektorConfig.cetpPort = Integer.parseInt(folder.getName()); - konnektorConfig.userConfigurations = new UserConfigurations(properties); - konnektorConfig.cardlinkEndpoint = new URI(properties.getProperty("cardlinkServerURL")); - konnektorConfig.subscriptionId = subscriptionId; - konnektorConfig.subscriptionTime = subscriptionTime; - konnektorConfig.folder = folder; - return konnektorConfig; - } catch (URISyntaxException | IOException e) { - String msg = String.format( - "Could not read konnektor config: folder=%s, subscriptionId=%s", folder.getName(), subscriptionId - ); - log.log(Level.WARNING, msg, e); - } - } - } - return null; - } - - @Override - public void saveSubscription(KonnektorConfig konnektorConfig, String subscriptionId, String error) { - try { - writeFile(konnektorConfig.getFolder().getAbsolutePath() + "/" + subscriptionId, error); - cleanUp(konnektorConfig, subscriptionId); - konnektorConfig.setSubscriptionId(subscriptionId); - konnektorConfig.setSubscriptionTime(OffsetDateTime.now()); - } catch (IOException e) { - String msg = String.format( - "Error while recreating subscription properties in folder: %s", - konnektorConfig.getFolder().getAbsolutePath() - ); - log.log(Level.SEVERE, msg, e); - } - } - - @Override - public void cleanUp(KonnektorConfig konnektorConfig, String subscriptionId) { - Arrays.stream(konnektorConfig.getFolder().listFiles()) - .filter(file -> !file.getName().equals(subscriptionId) && !file.getName().endsWith(PROPERTIES_EXT)) - .forEach(file -> { - boolean deleted = file.delete(); - if (!deleted) { - String msg = String.format("Unable to delete previous subscription file: %s", file.getName()); - log.log(Level.SEVERE, msg); - file.renameTo(new File(String.format("%s_DELETING", file.getAbsolutePath()))); - } - }); - } -} diff --git a/src/main/java/health/ere/ps/service/cetp/config/KonnektorConfig.java b/src/main/java/health/ere/ps/service/cetp/config/KonnektorConfig.java deleted file mode 100644 index 8e37b16e0..000000000 --- a/src/main/java/health/ere/ps/service/cetp/config/KonnektorConfig.java +++ /dev/null @@ -1,79 +0,0 @@ -package health.ere.ps.service.cetp.config; - -import health.ere.ps.model.config.UserConfigurations; - -import java.io.File; -import java.net.URI; -import java.time.OffsetDateTime; -import java.util.concurrent.Semaphore; - -public class KonnektorConfig { - - File folder; - Integer cetpPort; - URI cardlinkEndpoint; - String subscriptionId; - OffsetDateTime subscriptionTime; - UserConfigurations userConfigurations; - - private final Semaphore semaphore = new Semaphore(1); - - public KonnektorConfig() { - } - - public KonnektorConfig( - File folder, - Integer cetpPort, - UserConfigurations userConfigurations, - URI cardlinkEndpoint - ) { - this.folder = folder; - this.cetpPort = cetpPort; - this.userConfigurations = userConfigurations; - this.cardlinkEndpoint = cardlinkEndpoint; - - subscriptionId = null; - subscriptionTime = OffsetDateTime.now().minusDays(30); - } - - public Semaphore getSemaphore() { - return semaphore; - } - - public Integer getCetpPort() { - return cetpPort; - } - - public UserConfigurations getUserConfigurations() { - return userConfigurations; - } - - public String getHost() { - String connectorBaseURL = userConfigurations.getConnectorBaseURL(); - return connectorBaseURL == null ? null : connectorBaseURL.split("//")[1]; - } - - public URI getCardlinkEndpoint() { - return cardlinkEndpoint; - } - - public String getSubscriptionId() { - return subscriptionId; - } - - public void setSubscriptionId(String subscriptionId) { - this.subscriptionId = subscriptionId; - } - - public File getFolder() { - return folder; - } - - public OffsetDateTime getSubscriptionTime() { - return subscriptionTime; - } - - public void setSubscriptionTime(OffsetDateTime subscriptionTime) { - this.subscriptionTime = subscriptionTime; - } -} diff --git a/src/main/java/health/ere/ps/service/cetp/config/KonnektorConfigService.java b/src/main/java/health/ere/ps/service/cetp/config/KonnektorConfigService.java deleted file mode 100644 index 429d37bd3..000000000 --- a/src/main/java/health/ere/ps/service/cetp/config/KonnektorConfigService.java +++ /dev/null @@ -1,12 +0,0 @@ -package health.ere.ps.service.cetp.config; - -import java.util.Map; - -public interface KonnektorConfigService { - - Map loadConfigs(); - - void saveSubscription(KonnektorConfig konnektorConfig, String subscriptionId, String error); - - void cleanUp(KonnektorConfig konnektorConfig, String subscriptionId); -} diff --git a/src/main/java/health/ere/ps/service/cetp/mapper/DefaultMappingConfig.java b/src/main/java/health/ere/ps/service/cetp/mapper/DefaultMappingConfig.java new file mode 100644 index 000000000..fee23cd33 --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/mapper/DefaultMappingConfig.java @@ -0,0 +1,16 @@ +package health.ere.ps.service.cetp.mapper; + +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.MapperConfig; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.ReportingPolicy; + +@MapperConfig( + componentModel = "jakarta", + uses = {MapperUtils.class}, + nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, + unmappedTargetPolicy = ReportingPolicy.ERROR, + collectionMappingStrategy = CollectionMappingStrategy.TARGET_IMMUTABLE +) +public interface DefaultMappingConfig { +} diff --git a/src/main/java/health/ere/ps/service/cetp/mapper/DetailMapper.java b/src/main/java/health/ere/ps/service/cetp/mapper/DetailMapper.java new file mode 100644 index 000000000..32a29b6cc --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/mapper/DetailMapper.java @@ -0,0 +1,10 @@ +package health.ere.ps.service.cetp.mapper; + +import de.health.service.cetp.domain.fault.Detail; +import org.mapstruct.Mapper; + +@Mapper(config = DefaultMappingConfig.class) +public interface DetailMapper { + + Detail toDomain(de.gematik.ws.tel.error.v2.Error.Trace.Detail soap); +} diff --git a/src/main/java/health/ere/ps/service/cetp/mapper/ErrorMapper.java b/src/main/java/health/ere/ps/service/cetp/mapper/ErrorMapper.java new file mode 100644 index 000000000..cddd0025f --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/mapper/ErrorMapper.java @@ -0,0 +1,12 @@ +package health.ere.ps.service.cetp.mapper; + +import de.health.service.cetp.domain.fault.Error; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(config = DefaultMappingConfig.class, uses = {TraceMapper.class}) +public interface ErrorMapper { + + @Mapping(source = "messageID", target = "messageId") + Error toDomain(de.gematik.ws.tel.error.v2.Error soap); +} diff --git a/src/main/java/health/ere/ps/service/cetp/mapper/MapperUtils.java b/src/main/java/health/ere/ps/service/cetp/mapper/MapperUtils.java new file mode 100644 index 000000000..903717065 --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/mapper/MapperUtils.java @@ -0,0 +1,12 @@ +package health.ere.ps.service.cetp.mapper; + +import javax.xml.datatype.XMLGregorianCalendar; +import java.util.Date; + +@SuppressWarnings("unused") +public class MapperUtils { + + public static Date calendarToDate(XMLGregorianCalendar calendar) { + return calendar.toGregorianCalendar().getTime(); + } +} diff --git a/src/main/java/health/ere/ps/service/cetp/mapper/StatusMapper.java b/src/main/java/health/ere/ps/service/cetp/mapper/StatusMapper.java new file mode 100644 index 000000000..fc3c272e4 --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/mapper/StatusMapper.java @@ -0,0 +1,11 @@ +package health.ere.ps.service.cetp.mapper; + +import de.gematik.ws.conn.connectorcommon.v5.Status; +import de.health.service.cetp.domain.CetpStatus; +import org.mapstruct.Mapper; + +@Mapper(config = DefaultMappingConfig.class, uses = {ErrorMapper.class}) +public interface StatusMapper { + + CetpStatus toDomain(Status soap); +} diff --git a/src/main/java/health/ere/ps/service/cetp/mapper/SubscriptionMapper.java b/src/main/java/health/ere/ps/service/cetp/mapper/SubscriptionMapper.java new file mode 100644 index 000000000..602891a5f --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/mapper/SubscriptionMapper.java @@ -0,0 +1,12 @@ +package health.ere.ps.service.cetp.mapper; + +import de.health.service.cetp.domain.eventservice.Subscription; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(config = DefaultMappingConfig.class) +public interface SubscriptionMapper { + + @Mapping(source = "subscriptionID", target = "subscriptionId") + Subscription toDomain(de.gematik.ws.conn.eventservice.v7.SubscriptionType soap); +} diff --git a/src/main/java/health/ere/ps/service/cetp/mapper/SubscriptionResultMapper.java b/src/main/java/health/ere/ps/service/cetp/mapper/SubscriptionResultMapper.java new file mode 100644 index 000000000..f2e4bf156 --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/mapper/SubscriptionResultMapper.java @@ -0,0 +1,56 @@ +package health.ere.ps.service.cetp.mapper; + +import de.gematik.ws.conn.connectorcommon.v5.Status; +import de.gematik.ws.conn.eventservice.v7.SubscriptionRenewal; +import de.health.service.cetp.domain.CetpStatus; +import de.health.service.cetp.domain.SubscriptionResult; +import jakarta.xml.ws.Holder; +import org.mapstruct.AfterMapping; +import org.mapstruct.Context; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; + +import javax.xml.datatype.XMLGregorianCalendar; + +@Mapper(config = DefaultMappingConfig.class, uses = {StatusMapper.class}) +public abstract class SubscriptionResultMapper { + + StatusMapperImpl statusMapper = new StatusMapperImpl(); + + @Mapping(source = "subscriptionID", target = "subscriptionId") + @Mapping(target = "status", ignore = true) + public abstract SubscriptionResult toDomain(SubscriptionRenewal soap, @Context Holder status); + + @Mapping(target = "status", ignore = true) + @Mapping(target = "subscriptionId", ignore = true) + @Mapping(target = "terminationTime", ignore = true) + public abstract SubscriptionResult toDomain( + Object emptyInput, + @Context Holder status, + @Context Holder subscriptionId, + @Context Holder terminationTime + ); + + @AfterMapping + public void applyStatus( + @MappingTarget SubscriptionResult subscriptionResult, + @Context Holder status + ) { + CetpStatus domain = statusMapper.toDomain(status.value); + subscriptionResult.setStatus(domain); + } + + @AfterMapping + public void applyStatus( + @MappingTarget SubscriptionResult subscriptionResult, + @Context Holder status, + @Context Holder subscriptionId, + @Context Holder terminationTime + ) { + CetpStatus domain = statusMapper.toDomain(status.value); + subscriptionResult.setStatus(domain); + subscriptionResult.setSubscriptionId(subscriptionId.value); + subscriptionResult.setTerminationTime(terminationTime.value.toGregorianCalendar().getTime()); + } +} diff --git a/src/main/java/health/ere/ps/service/cetp/mapper/TraceMapper.java b/src/main/java/health/ere/ps/service/cetp/mapper/TraceMapper.java new file mode 100644 index 000000000..09a802c06 --- /dev/null +++ b/src/main/java/health/ere/ps/service/cetp/mapper/TraceMapper.java @@ -0,0 +1,12 @@ +package health.ere.ps.service.cetp.mapper; + +import de.health.service.cetp.domain.fault.Trace; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(config = DefaultMappingConfig.class, uses = {DetailMapper.class}) +public interface TraceMapper { + + @Mapping(source = "eventID", target = "eventId") + Trace toDomain(de.gematik.ws.tel.error.v2.Error.Trace soap); +} diff --git a/src/main/java/health/ere/ps/service/cetp/tracker/TrackerService.java b/src/main/java/health/ere/ps/service/cetp/tracker/TrackerService.java index 0ed984009..0c87120da 100644 --- a/src/main/java/health/ere/ps/service/cetp/tracker/TrackerService.java +++ b/src/main/java/health/ere/ps/service/cetp/tracker/TrackerService.java @@ -20,7 +20,7 @@ import java.util.logging.Logger; import java.util.stream.Collectors; -import static health.ere.ps.utils.Utils.terminateExecutor; +import static de.health.service.cetp.utils.Utils.terminateExecutor; @ApplicationScoped public class TrackerService { diff --git a/src/main/java/health/ere/ps/service/common/security/SecretsManagerService.java b/src/main/java/health/ere/ps/service/common/security/SecretsManagerService.java index 97bfbe12b..0d96dfecc 100644 --- a/src/main/java/health/ere/ps/service/common/security/SecretsManagerService.java +++ b/src/main/java/health/ere/ps/service/common/security/SecretsManagerService.java @@ -1,5 +1,21 @@ package health.ere.ps.service.common.security; +import de.health.service.cetp.FallbackSecretsManager; +import de.health.service.cetp.config.IUserConfigurations; +import health.ere.ps.config.AppConfig; +import health.ere.ps.exception.common.security.SecretsManagerException; +import health.ere.ps.model.config.UserConfigurations; +import health.ere.ps.service.config.UserConfigurationService; +import health.ere.ps.service.connector.endpoint.SSLUtilities; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -18,23 +34,8 @@ import java.util.logging.Level; import java.util.logging.Logger; -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; - -import health.ere.ps.config.AppConfig; -import health.ere.ps.exception.common.security.SecretsManagerException; -import health.ere.ps.model.config.UserConfigurations; -import health.ere.ps.service.config.UserConfigurationService; -import health.ere.ps.service.connector.endpoint.SSLUtilities; -import jakarta.annotation.PostConstruct; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Event; -import jakarta.inject.Inject; - @ApplicationScoped -public class SecretsManagerService { +public class SecretsManagerService implements FallbackSecretsManager { private static final Logger log = Logger.getLogger(SecretsManagerService.class.getName()); @@ -81,12 +82,11 @@ public SSLContext createSSLContext(UserConfigurations userConfigurations) { } } - public static byte[] getClientCertificateBytes(UserConfigurations userConfigurations) { + private byte[] getClientCertificateBytes(IUserConfigurations userConfigurations) { String base64UrlCertificate = userConfigurations.getClientCertificate(); String clientCertificateString = base64UrlCertificate.split(",")[1]; log.fine("Using certificate: "+clientCertificateString); - byte[] clientCertificateBytes = Base64.getDecoder().decode(clientCertificateString); - return clientCertificateBytes; + return Base64.getDecoder().decode(clientCertificateString); } public void acceptAllCertificates() { @@ -147,6 +147,7 @@ public SSLContext getSslContext() { return sslContext; } + @Override public KeyManagerFactory getKeyManagerFactory() { return keyManagerFactory; } diff --git a/src/main/java/health/ere/ps/service/connector/endpoint/EndpointDiscoveryService.java b/src/main/java/health/ere/ps/service/connector/endpoint/EndpointDiscoveryService.java index 09f0d6919..ea59c69c3 100644 --- a/src/main/java/health/ere/ps/service/connector/endpoint/EndpointDiscoveryService.java +++ b/src/main/java/health/ere/ps/service/connector/endpoint/EndpointDiscoveryService.java @@ -1,30 +1,29 @@ package health.ere.ps.service.connector.endpoint; -import java.io.IOException; -import java.io.InputStream; -import java.util.Base64; -import java.util.Optional; -import java.util.logging.Level; -import java.util.logging.Logger; - +import de.health.service.cetp.config.IUserConfigurations; +import de.health.service.cetp.config.UserRuntimeConfig; +import health.ere.ps.config.AppConfig; +import health.ere.ps.service.common.security.SecretsManagerService; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.ProcessingException; import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.client.Invocation; import jakarta.ws.rs.client.Invocation.Builder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - import org.eclipse.microprofile.config.inject.ConfigProperty; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import health.ere.ps.config.AppConfig; -import health.ere.ps.config.UserConfig; -import health.ere.ps.service.common.security.SecretsManagerService; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Base64; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; /** * This service automatically discovers the endpoints that are available at the connector. @@ -49,7 +48,7 @@ public class EndpointDiscoveryService { @Inject AppConfig appConfig; @Inject - UserConfig userConfig; + UserRuntimeConfig userConfig; @Inject SecretsManagerService secretsManagerService; @@ -64,7 +63,7 @@ public EndpointDiscoveryService() { } - public EndpointDiscoveryService(UserConfig userConfig, SecretsManagerService secretsManagerService) { + public EndpointDiscoveryService(UserRuntimeConfig userConfig, SecretsManagerService secretsManagerService) { this.userConfig = userConfig; this.secretsManagerService = secretsManagerService; } @@ -92,8 +91,9 @@ public void obtainConfiguration(boolean throwEndpointException) throws IOExcepti .path("/connector.sds") .request(); - String basicAuthUsername = userConfig.getConfigurations().getBasicAuthUsername(); - String basicAuthPassword = userConfig.getConfigurations().getBasicAuthPassword(); + IUserConfigurations userConfigurations = userConfig.getUserConfigurations(); + String basicAuthUsername = userConfigurations.getBasicAuthUsername(); + String basicAuthPassword = userConfigurations.getBasicAuthPassword(); if(basicAuthUsername != null && !basicAuthUsername.equals("")) { builder.header("Authorization", "Basic "+Base64.getEncoder().encodeToString((basicAuthUsername+":"+basicAuthPassword).getBytes())); } @@ -208,10 +208,10 @@ private void extractAndSetConnectorVersion(Document document) { if (versionContainingText.contains("PTV4+") || versionContainingText.contains("PTV4Plus")) { log.info("Connector version PTV4+ found in connector.sds"); - userConfig.getConfigurations().setVersion("PTV4+"); + userConfig.getUserConfigurations().setVersion("PTV4+"); } else if (versionContainingText.contains("PTV4")) { log.info("Connector version PTV4 found in connector.sds"); - userConfig.getConfigurations().setVersion("PTV4"); + userConfig.getUserConfigurations().setVersion("PTV4"); } else { log.warning("Could not determine the version of the connector to use from connector.sds, " + "using the one from the configuration:" + userConfig.getConnectorVersion()); diff --git a/src/main/java/health/ere/ps/service/connector/provider/AbstractConnectorServicesProvider.java b/src/main/java/health/ere/ps/service/connector/provider/AbstractConnectorServicesProvider.java index ff1ff4b29..447a63356 100644 --- a/src/main/java/health/ere/ps/service/connector/provider/AbstractConnectorServicesProvider.java +++ b/src/main/java/health/ere/ps/service/connector/provider/AbstractConnectorServicesProvider.java @@ -1,14 +1,5 @@ package health.ere.ps.service.connector.provider; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import jakarta.inject.Inject; -import jakarta.xml.ws.BindingProvider; -import javax.net.ssl.SSLContext; -import javax.xml.parsers.ParserConfigurationException; - import de.gematik.ws.conn.authsignatureservice.wsdl.v7.AuthSignatureService; import de.gematik.ws.conn.authsignatureservice.wsdl.v7.AuthSignatureServicePortType; import de.gematik.ws.conn.cardservice.wsdl.v8.CardService; @@ -24,11 +15,18 @@ import de.gematik.ws.conn.signatureservice.wsdl.v7.SignatureServiceV755; import de.gematik.ws.conn.vsds.vsdservice.v5.VSDService; import de.gematik.ws.conn.vsds.vsdservice.v5.VSDServicePortType; -import health.ere.ps.config.UserConfig; +import de.health.service.cetp.config.IUserConfigurations; +import de.health.service.cetp.config.UserRuntimeConfig; import health.ere.ps.config.interceptor.ProvidedConfig; import health.ere.ps.service.common.security.SecretsManagerService; import health.ere.ps.service.connector.endpoint.EndpointDiscoveryService; import health.ere.ps.service.connector.endpoint.SSLUtilities; +import jakarta.inject.Inject; +import jakarta.xml.ws.BindingProvider; + +import javax.net.ssl.SSLContext; +import java.util.logging.Level; +import java.util.logging.Logger; public abstract class AbstractConnectorServicesProvider { private final static Logger log = Logger.getLogger(AbstractConnectorServicesProvider.class.getName()); @@ -213,8 +211,9 @@ private void configureBindingProvider(BindingProvider bindingProvider) { bindingProvider.getRequestContext().put("com.sun.xml.ws.transport.https.client.hostname.verifier", new SSLUtilities.FakeHostnameVerifier()); - String basicAuthUsername = getUserConfig().getConfigurations().getBasicAuthUsername(); - String basicAuthPassword = getUserConfig().getConfigurations().getBasicAuthPassword(); + IUserConfigurations userConfigurations = getUserConfig().getUserConfigurations(); + String basicAuthUsername = userConfigurations.getBasicAuthUsername(); + String basicAuthPassword = userConfigurations.getBasicAuthPassword(); if(basicAuthUsername != null && !basicAuthUsername.equals("")) { bindingProvider.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, basicAuthUsername); @@ -262,5 +261,5 @@ public ContextType getContextType() { return contextType; } - public abstract UserConfig getUserConfig(); + public abstract UserRuntimeConfig getUserConfig(); } diff --git a/src/main/java/health/ere/ps/service/connector/provider/DefaultConnectorServicesProvider.java b/src/main/java/health/ere/ps/service/connector/provider/DefaultConnectorServicesProvider.java index 9ff5fa518..ffd822f09 100644 --- a/src/main/java/health/ere/ps/service/connector/provider/DefaultConnectorServicesProvider.java +++ b/src/main/java/health/ere/ps/service/connector/provider/DefaultConnectorServicesProvider.java @@ -1,16 +1,15 @@ package health.ere.ps.service.connector.provider; +import de.health.service.cetp.config.UserRuntimeConfig; import jakarta.annotation.PostConstruct; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import health.ere.ps.config.UserConfig; - @ApplicationScoped public class DefaultConnectorServicesProvider extends AbstractConnectorServicesProvider { @Inject - UserConfig userConfig; + UserRuntimeConfig userConfig; @PostConstruct void init() { @@ -18,7 +17,7 @@ void init() { } @Override - public UserConfig getUserConfig() { + public UserRuntimeConfig getUserConfig() { return userConfig; } diff --git a/src/main/java/health/ere/ps/service/connector/provider/MultiConnectorServicesProvider.java b/src/main/java/health/ere/ps/service/connector/provider/MultiConnectorServicesProvider.java index 176249d63..cd782cf06 100644 --- a/src/main/java/health/ere/ps/service/connector/provider/MultiConnectorServicesProvider.java +++ b/src/main/java/health/ere/ps/service/connector/provider/MultiConnectorServicesProvider.java @@ -8,8 +8,8 @@ import de.gematik.ws.conn.signatureservice.wsdl.v7.SignatureServicePortTypeV740; import de.gematik.ws.conn.signatureservice.wsdl.v7.SignatureServicePortTypeV755; import de.gematik.ws.conn.vsds.vsdservice.v5.VSDServicePortType; +import de.health.service.cetp.config.UserRuntimeConfig; import health.ere.ps.config.SimpleUserConfig; -import health.ere.ps.config.UserConfig; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Event; import jakarta.inject.Inject; @@ -17,7 +17,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; @ApplicationScoped @@ -34,7 +33,7 @@ public class MultiConnectorServicesProvider { //Map singleConnectorServicesProvider = new ConcurrentHashMap<>(); Map singleConnectorServicesProvider = Collections.synchronizedMap(new HashMap()); - public AbstractConnectorServicesProvider getSingleConnectorServicesProvider(UserConfig userConfig) { + public AbstractConnectorServicesProvider getSingleConnectorServicesProvider(UserRuntimeConfig userConfig) { if (userConfig == null) { return defaultConnectorServicesProvider; } else { @@ -48,35 +47,35 @@ public AbstractConnectorServicesProvider getSingleConnectorServicesProvider(User } } - public CardServicePortType getCardServicePortType(UserConfig userConfig) { + public CardServicePortType getCardServicePortType(UserRuntimeConfig userConfig) { return getSingleConnectorServicesProvider(userConfig).getCardServicePortType(); } - public CertificateServicePortType getCertificateServicePortType(UserConfig userConfig) { + public CertificateServicePortType getCertificateServicePortType(UserRuntimeConfig userConfig) { return getSingleConnectorServicesProvider(userConfig).getCertificateService(); } - public EventServicePortType getEventServicePortType(UserConfig userConfig) { + public EventServicePortType getEventServicePortType(UserRuntimeConfig userConfig) { return getSingleConnectorServicesProvider(userConfig).getEventServicePortType(); } - public AuthSignatureServicePortType getAuthSignatureServicePortType(UserConfig userConfig) { + public AuthSignatureServicePortType getAuthSignatureServicePortType(UserRuntimeConfig userConfig) { return getSingleConnectorServicesProvider(userConfig).getAuthSignatureServicePortType(); } - public SignatureServicePortTypeV740 getSignatureServicePortType(UserConfig userConfig) { + public SignatureServicePortTypeV740 getSignatureServicePortType(UserRuntimeConfig userConfig) { return getSingleConnectorServicesProvider(userConfig).getSignatureServicePortType(); } - public SignatureServicePortTypeV755 getSignatureServicePortTypeV755(UserConfig userConfig) { + public SignatureServicePortTypeV755 getSignatureServicePortTypeV755(UserRuntimeConfig userConfig) { return getSingleConnectorServicesProvider(userConfig).getSignatureServicePortTypeV755(); } - public VSDServicePortType getVSDServicePortType(UserConfig userConfig) { + public VSDServicePortType getVSDServicePortType(UserRuntimeConfig userConfig) { return getSingleConnectorServicesProvider(userConfig).getVSDServicePortType(); } - public ContextType getContextType(UserConfig userConfig) { + public ContextType getContextType(UserRuntimeConfig userConfig) { if (userConfig == null) { return defaultConnectorServicesProvider.getContextType(); } diff --git a/src/main/java/health/ere/ps/service/connector/provider/SingleConnectorServicesProvider.java b/src/main/java/health/ere/ps/service/connector/provider/SingleConnectorServicesProvider.java index 1f9923525..3684bd2ef 100644 --- a/src/main/java/health/ere/ps/service/connector/provider/SingleConnectorServicesProvider.java +++ b/src/main/java/health/ere/ps/service/connector/provider/SingleConnectorServicesProvider.java @@ -1,7 +1,8 @@ package health.ere.ps.service.connector.provider; +import de.health.service.cetp.config.IUserConfigurations; +import de.health.service.cetp.config.UserRuntimeConfig; import health.ere.ps.config.AppConfig; -import health.ere.ps.config.UserConfig; import health.ere.ps.service.common.security.SecretsManagerService; import health.ere.ps.service.connector.endpoint.EndpointDiscoveryService; import jakarta.enterprise.event.Event; @@ -27,15 +28,16 @@ public class SingleConnectorServicesProvider extends AbstractConnectorServicesProvider { private final static Logger log = Logger.getLogger(SingleConnectorServicesProvider.class.getName()); - UserConfig userConfig; + UserRuntimeConfig userConfig; - public SingleConnectorServicesProvider(UserConfig userConfig, Event exceptionEvent) { + public SingleConnectorServicesProvider(UserRuntimeConfig userConfig, Event exceptionEvent) { this.userConfig = userConfig; this.secretsManagerService = new SecretsManagerService(); // Try to read SSL Certificates from the userConfig (this can also be the runtime config) - String configKeystoreUri = userConfig.getConfigurations().getClientCertificate(); - String configKeystorePass = userConfig.getConfigurations().getClientCertificatePassword(); + IUserConfigurations userConfigurations = userConfig.getUserConfigurations(); + String configKeystoreUri = userConfigurations.getClientCertificate(); + String configKeystorePass = userConfigurations.getClientCertificatePassword(); if (configKeystoreUri != null && !configKeystoreUri.isEmpty()) { try { @@ -159,7 +161,7 @@ public String[] getServerAliases(String arg0, Principal[] arg1) { } } - public UserConfig getUserConfig() { + public UserRuntimeConfig getUserConfig() { return userConfig; } } diff --git a/src/main/java/health/ere/ps/service/gematik/PharmacyService.java b/src/main/java/health/ere/ps/service/gematik/PharmacyService.java index d69468f7b..5c311058c 100644 --- a/src/main/java/health/ere/ps/service/gematik/PharmacyService.java +++ b/src/main/java/health/ere/ps/service/gematik/PharmacyService.java @@ -5,19 +5,17 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; import de.gematik.ws.conn.cardservicecommon.v2.CardTypeType; import de.gematik.ws.conn.connectorcontext.v2.ContextType; -import de.gematik.ws.conn.eventservice.v7.SubscriptionType; import de.gematik.ws.conn.eventservice.wsdl.v7.EventServicePortType; import de.gematik.ws.conn.vsds.vsdservice.v5.FaultMessage; import de.gematik.ws.conn.vsds.vsdservice.v5.VSDServicePortType; import de.gematik.ws.conn.vsds.vsdservice.v5.VSDStatusType; +import de.health.service.cetp.IKonnektorClient; +import de.health.service.cetp.domain.eventservice.Subscription; import health.ere.ps.config.AppConfig; import health.ere.ps.config.RuntimeConfig; import health.ere.ps.jmx.ReadEPrescriptionsMXBeanImpl; -import health.ere.ps.service.cetp.KonnektorClient; import health.ere.ps.service.connector.provider.MultiConnectorServicesProvider; import health.ere.ps.service.fhir.FHIRService; import health.ere.ps.service.idp.BearerTokenService; @@ -56,8 +54,11 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.time.Duration; -import java.util.*; +import java.util.Base64; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; @@ -68,7 +69,6 @@ import java.util.stream.Stream; import java.util.zip.GZIPInputStream; - /* Note: reading, writing and resending of failed rejects are done by one Thread (see scheduledExecutorService), no additional synchronization for retrying reject is need */ @ApplicationScoped public class PharmacyService implements AutoCloseable { @@ -87,7 +87,7 @@ public class PharmacyService implements AutoCloseable { AppConfig appConfig; @Inject - KonnektorClient konnektorClient; + IKonnektorClient konnektorClient; @Inject MultiConnectorServicesProvider connectorServicesProvider; @@ -196,9 +196,9 @@ public Holder readVSD( try { String listString; try { - List subscriptions = konnektorClient.getSubscriptions(runtimeConfig); + List subscriptions = konnektorClient.getSubscriptions(runtimeConfig); listString = subscriptions.stream() - .map(s -> String.format("[id=%s eventTo=%s topic=%s]", s.getSubscriptionID(), s.getEventTo(), s.getTopic())) + .map(s -> String.format("[id=%s eventTo=%s topic=%s]", s.getSubscriptionId(), s.getEventTo(), s.getTopic())) .collect(Collectors.joining(",")); } catch (Throwable e) { String msg = String.format("[%s] Could not get active getSubscriptions", correlationId); diff --git a/src/main/java/health/ere/ps/service/health/check/CardlinkWebsocketCheck.java b/src/main/java/health/ere/ps/service/health/check/CardlinkWebsocketCheck.java index 6d339942f..8672b7153 100644 --- a/src/main/java/health/ere/ps/service/health/check/CardlinkWebsocketCheck.java +++ b/src/main/java/health/ere/ps/service/health/check/CardlinkWebsocketCheck.java @@ -2,10 +2,11 @@ import health.ere.ps.config.RuntimeConfig; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.Dependent; import java.util.Map; -@ApplicationScoped +@Dependent public class CardlinkWebsocketCheck implements Check { private boolean connected; diff --git a/src/main/java/health/ere/ps/service/health/check/CetpServerCheck.java b/src/main/java/health/ere/ps/service/health/check/CetpServerCheck.java index 3d39edaab..9ff68162f 100644 --- a/src/main/java/health/ere/ps/service/health/check/CetpServerCheck.java +++ b/src/main/java/health/ere/ps/service/health/check/CetpServerCheck.java @@ -1,7 +1,7 @@ package health.ere.ps.service.health.check; +import de.health.service.cetp.CETPServer; import health.ere.ps.config.RuntimeConfig; -import health.ere.ps.service.cetp.CETPServer; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; diff --git a/src/main/java/health/ere/ps/utils/Utils.java b/src/main/java/health/ere/ps/utils/Utils.java deleted file mode 100644 index 7c20b2278..000000000 --- a/src/main/java/health/ere/ps/utils/Utils.java +++ /dev/null @@ -1,90 +0,0 @@ -package health.ere.ps.utils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.Optional; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Predicate; - -public class Utils { - - private static final Logger log = LoggerFactory.getLogger(Utils.class); - - public static void writeFile(String absolutePath, String content) throws IOException { - try (FileOutputStream os = new FileOutputStream(absolutePath)) { - if (content != null) { - os.write(content.getBytes()); - } - os.flush(); - } - } - - public static boolean deleteFiles(File folder, Predicate predicate) { - AtomicBoolean result = new AtomicBoolean(true); - Arrays.stream(folder.listFiles()) - .filter(predicate) - .forEach(file -> result.set(result.get() & file.delete())); - return result.get(); - } - - public static String printException(Throwable e) { - StringWriter sw = new StringWriter(); - e.printStackTrace(new PrintWriter(sw)); - String stacktrace = sw.toString(); - return e.getMessage() + " -> " + stacktrace; - } - - public static Optional getHostFromNetworkInterfaces() { - Inet4Address localAddress = null; - try { - Enumeration e = NetworkInterface.getNetworkInterfaces(); - while (e.hasMoreElements()) { - NetworkInterface n = e.nextElement(); - Enumeration ee = n.getInetAddresses(); - while (ee.hasMoreElements()) { - InetAddress i = ee.nextElement(); - if (i instanceof Inet4Address && i.getAddress()[0] != 127) { - localAddress = (Inet4Address) i; - break; - } - } - } - } catch (SocketException ignored) { - } - return localAddress == null - ? Optional.empty() - : Optional.of(localAddress.getHostAddress()); - } - - public static void terminateExecutor(ExecutorService executorService, String executorName, int awaitMillis) { - if (executorService != null) { - log.info(String.format("[%s] Terminating", executorName)); - executorService.shutdown(); - try { - if (!executorService.awaitTermination(awaitMillis, TimeUnit.MILLISECONDS)) { - executorService.shutdownNow(); - if (!executorService.awaitTermination(awaitMillis, TimeUnit.MILLISECONDS)) { - log.info(String.format("[%s] Is not terminated", executorName)); - } - } - } catch (InterruptedException ex) { - executorService.shutdownNow(); - Thread.currentThread().interrupt(); - } - } - } -} diff --git a/src/main/resources/META-INF/resources/frontend b/src/main/resources/META-INF/resources/frontend index 509e4a807..7a1eb2279 160000 --- a/src/main/resources/META-INF/resources/frontend +++ b/src/main/resources/META-INF/resources/frontend @@ -1 +1 @@ -Subproject commit 509e4a807b428b0edb40ac28373d6b92462a44e8 +Subproject commit 7a1eb2279df59a3696acd61b7d9bb78baf4d330f diff --git a/src/test/java/health/ere/ps/service/cetp/KonnektorFailedUnsubscriptionTest.java b/src/test/java/health/ere/ps/service/cetp/KonnektorFailedUnsubscriptionTest.java index 38a65fb84..ca5cb1d3b 100644 --- a/src/test/java/health/ere/ps/service/cetp/KonnektorFailedUnsubscriptionTest.java +++ b/src/test/java/health/ere/ps/service/cetp/KonnektorFailedUnsubscriptionTest.java @@ -5,8 +5,9 @@ import de.gematik.ws.conn.eventservice.wsdl.v7.EventServicePortType; import de.gematik.ws.conn.eventservice.wsdl.v7.FaultMessage; import de.gematik.ws.tel.error.v2.Error; +import de.health.service.cetp.SubscriptionManager; +import de.health.service.cetp.konnektorconfig.FSConfigService; import health.ere.ps.profile.RUDevTestProfile; -import health.ere.ps.service.cetp.config.FSConfigService; import health.ere.ps.service.connector.provider.MultiConnectorServicesProvider; import io.quarkus.test.junit.QuarkusMock; import io.quarkus.test.junit.QuarkusTest; @@ -28,8 +29,8 @@ import java.util.List; import java.util.UUID; -import static health.ere.ps.utils.Utils.deleteFiles; -import static health.ere.ps.utils.Utils.writeFile; +import static de.health.service.cetp.utils.Utils.deleteFiles; +import static de.health.service.cetp.utils.Utils.writeFile; import static io.restassured.RestAssured.given; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; diff --git a/src/test/java/health/ere/ps/service/cetp/LocalAddressInSameSubnetFinderTest.java b/src/test/java/health/ere/ps/service/cetp/LocalAddressInSameSubnetFinderTest.java deleted file mode 100644 index f394b05dd..000000000 --- a/src/test/java/health/ere/ps/service/cetp/LocalAddressInSameSubnetFinderTest.java +++ /dev/null @@ -1,125 +0,0 @@ -package health.ere.ps.service.cetp; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import java.net.*; -import java.util.Arrays; -import java.util.List; - -public class LocalAddressInSameSubnetFinderTest { - - @Test - public void subnetMatch() throws UnknownHostException { - LocalAddressInSameSubnetFinder ipFinder = new LocalAddressInSameSubnetFinder(); - Assertions.assertTrue(ipFinder.isInSubnet( - createInterfaceAddress( - InetAddress.getByName("192.168.178.15"), - InetAddress.getByName("192.168.178.255"), - (short) 24 - ), - InetAddress.getByName("192.168.178.200") - )); - } - - @Test - public void subnetSmallMaskNoMatch() throws UnknownHostException { - LocalAddressInSameSubnetFinder ipFinder = new LocalAddressInSameSubnetFinder(); - Assertions.assertFalse(ipFinder.isInSubnet( - createInterfaceAddress( - InetAddress.getByName("192.168.178.14"), - InetAddress.getByName("192.168.178.15"), - (short) 30 - ), - InetAddress.getByName("192.168.178.200") - )); - } - - @Test - public void allSubnetMatch() throws UnknownHostException { - LocalAddressInSameSubnetFinder ipFinder = new LocalAddressInSameSubnetFinder(); - Assertions.assertTrue(ipFinder.isInSubnet( - createInterfaceAddress( - InetAddress.getByName("192.168.178.15"), - InetAddress.getByName("255.255.255.255"), - (short) 0 - ), - InetAddress.getByName("10.10.13.15") - )); - } - - @Test - public void localIPskipLoopback() throws Exception { - LocalAddressInSameSubnetFinder ipFinder = new LocalAddressInSameSubnetFinder(); - Inet4Address result = ipFinder.findHostAddress( - List.of( - createNetworkInterface( - true, true, - createInterfaceAddress( - InetAddress.getByName("192.168.178.10"), - InetAddress.getByName("192.168.178.255"), - (short) 24 - ) - ), - createNetworkInterface( - false, true, - createInterfaceAddress( - InetAddress.getByName("192.168.10.10"), - InetAddress.getByName("192.168.255.255"), - (short) 16 - ) - ) - ), - (Inet4Address) InetAddress.getByName("192.168.10.15") - ); - - // If first NIC wasn't a loopback, the first should have matched due to smaller subnet/better match. - Assertions.assertEquals( "192.168.10.10", result.getHostAddress()); - } - - @Test - public void localIPbestMatch() throws Exception { - LocalAddressInSameSubnetFinder serviceDiscoveryRequestHandler = new LocalAddressInSameSubnetFinder(); - Inet4Address result = serviceDiscoveryRequestHandler.findHostAddress( - List.of( - createNetworkInterface( - false, true, - createInterfaceAddress( - InetAddress.getByName("192.168.178.10"), - InetAddress.getByName("192.168.178.255"), - (short) 24 - ) - ), - createNetworkInterface( - false, true, - createInterfaceAddress( - InetAddress.getByName("192.168.10.10"), - InetAddress.getByName("192.168.255.255"), - (short) 16 - ) - ) - ), - (Inet4Address) InetAddress.getByName("192.168.178.15") - ); - - Assertions.assertEquals(result.getHostAddress(), "192.168.178.10"); - } - - private NetworkInterface createNetworkInterface(boolean isLoopback, boolean isUp, InterfaceAddress... ifAddresses) throws SocketException { - NetworkInterface result = Mockito.mock(NetworkInterface.class); - Mockito.when(result.isLoopback()).thenReturn(isLoopback); - Mockito.when(result.isUp()).thenReturn(isUp); - Mockito.when(result.getInterfaceAddresses()).thenReturn(Arrays.asList(ifAddresses)); - return result; - } - - - private InterfaceAddress createInterfaceAddress(InetAddress address, InetAddress broadcast, short networkPrefixLength) { - InterfaceAddress result = Mockito.mock(InterfaceAddress.class); - Mockito.when(result.getAddress()).thenReturn(address); - Mockito.when(result.getBroadcast()).thenReturn(broadcast); - Mockito.when(result.getNetworkPrefixLength()).thenReturn(networkPrefixLength); - return result; - } -} diff --git a/src/test/java/health/ere/ps/service/cetp/SubscriptionRenewalTest.java b/src/test/java/health/ere/ps/service/cetp/SubscriptionRenewalTest.java index d70fbf9c7..58b5963f1 100644 --- a/src/test/java/health/ere/ps/service/cetp/SubscriptionRenewalTest.java +++ b/src/test/java/health/ere/ps/service/cetp/SubscriptionRenewalTest.java @@ -8,8 +8,9 @@ import de.gematik.ws.conn.eventservice.v7.SubscriptionType; import de.gematik.ws.conn.eventservice.wsdl.v7.EventServicePortType; import de.gematik.ws.tel.error.v2.Error; +import de.health.service.cetp.SubscriptionManager; +import de.health.service.cetp.konnektorconfig.KonnektorConfig; import health.ere.ps.profile.RUDevTestProfile; -import health.ere.ps.service.cetp.config.KonnektorConfig; import health.ere.ps.service.connector.provider.MultiConnectorServicesProvider; import io.quarkus.test.junit.QuarkusMock; import io.quarkus.test.junit.QuarkusTest; @@ -30,7 +31,7 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; -import static health.ere.ps.utils.Utils.getHostFromNetworkInterfaces; +import static de.health.service.cetp.utils.Utils.getHostFromNetworkInterfaces; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; diff --git a/src/test/java/health/ere/ps/service/cetp/config/KonnektorConfigTest.java b/src/test/java/health/ere/ps/service/cetp/config/KonnektorConfigTest.java index 52e15fc3a..e06597756 100644 --- a/src/test/java/health/ere/ps/service/cetp/config/KonnektorConfigTest.java +++ b/src/test/java/health/ere/ps/service/cetp/config/KonnektorConfigTest.java @@ -1,7 +1,15 @@ package health.ere.ps.service.cetp.config; +import de.health.service.cetp.SubscriptionManager; +import de.health.service.cetp.konnektorconfig.FSConfigService; +import de.health.service.cetp.konnektorconfig.KonnektorConfig; +import health.ere.ps.config.AppConfig; +import health.ere.ps.config.UserConfig; import health.ere.ps.model.config.UserConfigurations; -import health.ere.ps.service.cetp.SubscriptionManager; +import health.ere.ps.profile.RUDevTestProfile; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.TestProfile; +import jakarta.inject.Inject; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -16,12 +24,20 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +@QuarkusTest +@TestProfile(RUDevTestProfile.class) public class KonnektorConfigTest { + @Inject + AppConfig appConfig; + + @Inject + UserConfig userConfig; + @Test void testGenerateKonnektorConfig() { - FSConfigService configService = new FSConfigService(); - configService.configFolder = "src/test/resources/config/konnektoren/"; + FSConfigService configService = new FSConfigService(appConfig, userConfig); + configService.setConfigFolder("src/test/resources/config/konnektoren/"); var configs = configService.loadConfigs(); assertEquals(3, configs.size()); @@ -42,8 +58,8 @@ void testGenerateKonnektorConfig() { @Test public void twoConfigsWithSameKonnectorAreLoaded() { - FSConfigService configService = spy(new FSConfigService()); - configService.configFolder = "config/konnektoren"; + FSConfigService configService = spy(new FSConfigService(appConfig, userConfig)); + configService.setConfigFolder("config/konnektoren"); List sameKonnektorConfigs = new ArrayList<>(); Properties sameKonnektorProperties = new Properties(); @@ -54,7 +70,7 @@ public void twoConfigsWithSameKonnectorAreLoaded() { doReturn(sameKonnektorConfigs).when(configService).readFromPath(any()); - SubscriptionManager subscriptionManager = new SubscriptionManager(null, null, configService); + SubscriptionManager subscriptionManager = new SubscriptionManager(appConfig, userConfig, null, configService); subscriptionManager.onStart(null); Collection konnektorConfigs = subscriptionManager.getKonnektorConfigs(konnektorHost); assertEquals(sameKonnektorConfigs.size(), konnektorConfigs.size()); diff --git a/src/test/java/health/ere/ps/service/cetp/config/KonnektorSubscriptionTest.java b/src/test/java/health/ere/ps/service/cetp/config/KonnektorSubscriptionTest.java index 41e83c21f..7e3967d90 100644 --- a/src/test/java/health/ere/ps/service/cetp/config/KonnektorSubscriptionTest.java +++ b/src/test/java/health/ere/ps/service/cetp/config/KonnektorSubscriptionTest.java @@ -3,10 +3,10 @@ import de.gematik.ws.conn.connectorcommon.v5.Status; import de.gematik.ws.conn.connectorcontext.v2.ContextType; import de.gematik.ws.conn.eventservice.wsdl.v7.EventServicePortType; +import de.health.service.cetp.SubscriptionManager; +import de.health.service.cetp.konnektorconfig.FSConfigService; +import de.health.service.cetp.konnektorconfig.KonnektorConfigService; import health.ere.ps.profile.RUDevTestProfile; -import health.ere.ps.service.cetp.SubscriptionManager; -import health.ere.ps.service.cetp.config.FSConfigService; -import health.ere.ps.service.cetp.config.KonnektorConfigService; import health.ere.ps.service.connector.provider.MultiConnectorServicesProvider; import io.quarkus.test.junit.QuarkusMock; import io.quarkus.test.junit.QuarkusTest; @@ -106,7 +106,7 @@ public void beforeEach() throws Exception { new File(TEMP_CONFIG).delete(); if (konnektorConfigService instanceof FSConfigService fsConfigService) { - fsConfigService.configFolder = "config/konnektoren"; + fsConfigService.setConfigFolder("config/konnektoren"); } } @@ -150,7 +150,7 @@ public void fakeFolderConfigKonnektorSubscriptionReloadedWhenHostMatchesAppConfi Pair pair = prepareTempConfigFolder(); if (pair.getKey()) { if (konnektorConfigService instanceof FSConfigService fsConfigService) { - fsConfigService.configFolder = pair.getValue(); + fsConfigService.setConfigFolder(pair.getValue()); subscriptionManager.onStart(null); } Response response = given() @@ -172,7 +172,7 @@ public void fakeFolderConfigKonnektorSubscriptionNotReloadedWhenHostDoesntMatchA Pair pair = prepareTempConfigFolder(); if (pair.getKey()) { if (konnektorConfigService instanceof FSConfigService fsConfigService) { - fsConfigService.configFolder = pair.getValue(); + fsConfigService.setConfigFolder(pair.getValue()); subscriptionManager.onStart(null); } String host = "192.168.178.52"; diff --git a/src/test/java/health/ere/ps/service/cetp/tracker/TrackerTest.java b/src/test/java/health/ere/ps/service/cetp/tracker/TrackerTest.java index 77888b64e..428de4c37 100644 --- a/src/test/java/health/ere/ps/service/cetp/tracker/TrackerTest.java +++ b/src/test/java/health/ere/ps/service/cetp/tracker/TrackerTest.java @@ -20,8 +20,8 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Logger; +import static de.health.service.cetp.utils.Utils.deleteFiles; import static health.ere.ps.service.cetp.tracker.TrackerService.REQUESTS_CSV; -import static health.ere.ps.utils.Utils.deleteFiles; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue;