From 5f7f7b41cd0705a0934fb9818ee188315481cf9f Mon Sep 17 00:00:00 2001 From: Eddie Carpenter Date: Wed, 3 Jul 2024 01:13:38 +1200 Subject: [PATCH] Added Quarkus support --- .../deployment/pom.xml | 49 ++ .../deployment/DiameterClientProcessor.java | 91 +++ .../diameter-client-quarkus/pom.xml | 64 ++ .../diameter-client-quarkus/runtime/pom.xml | 78 ++ .../java/io/go/diameter/DiameterClient.java | 57 ++ .../runtime/DiameterClientRecorder.java | 36 + .../resources/META-INF/quarkus-extension.yaml | 9 + .../diameter-quarkus-config/pom.xml | 52 ++ .../diameter/config/DiameterClientConfig.java | 40 + .../config/DiameterClientConfiguration.java | 280 +++++++ .../io/go/diameter/config/DiameterConfig.java | 39 + .../diameter/config/DiameterServerConfig.java | 11 + .../config/DiameterServerConfiguration.java | 132 ++++ .../config/mapping/ApplicationId.java | 30 + .../diameter/config/mapping/Concurrent.java | 57 ++ .../go/diameter/config/mapping/Extension.java | 140 ++++ .../go/diameter/config/mapping/LocalPeer.java | 63 ++ .../go/diameter/config/mapping/Network.java | 20 + .../config/mapping/OverloadMonitor.java | 30 + .../go/diameter/config/mapping/Parameter.java | 136 ++++ .../io/go/diameter/config/mapping/Peer.java | 42 ++ .../io/go/diameter/config/mapping/Realm.java | 50 ++ .../deployment/pom.xml | 49 ++ .../deployment/DiameterServerProcessor.java | 50 ++ .../diameter-server-quarkus/pom.xml | 64 ++ .../diameter-server-quarkus/runtime/pom.xml | 89 +++ .../java/io/go/diameter/DiameterServer.java | 38 + .../runtime/DiameterServerConfigProducer.java | 23 + .../runtime/DiameterServerRecorder.java | 37 + .../resources/META-INF/quarkus-extension.yaml | 9 + diameter-quarkus/pom.xml | 103 +++ .../org/example/server/ExampleServer.java | 710 +++++++++--------- lombok.config | 1 + pom.xml | 37 + 34 files changed, 2371 insertions(+), 345 deletions(-) create mode 100644 diameter-quarkus/diameter-client-quarkus/deployment/pom.xml create mode 100644 diameter-quarkus/diameter-client-quarkus/deployment/src/main/java/io/go/diameter/client/deployment/DiameterClientProcessor.java create mode 100644 diameter-quarkus/diameter-client-quarkus/pom.xml create mode 100644 diameter-quarkus/diameter-client-quarkus/runtime/pom.xml create mode 100644 diameter-quarkus/diameter-client-quarkus/runtime/src/main/java/io/go/diameter/DiameterClient.java create mode 100644 diameter-quarkus/diameter-client-quarkus/runtime/src/main/java/io/go/diameter/client/runtime/DiameterClientRecorder.java create mode 100644 diameter-quarkus/diameter-client-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 diameter-quarkus/diameter-quarkus-config/pom.xml create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterClientConfig.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterClientConfiguration.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterConfig.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterServerConfig.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterServerConfiguration.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/ApplicationId.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Concurrent.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Extension.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/LocalPeer.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Network.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/OverloadMonitor.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Parameter.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Peer.java create mode 100644 diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Realm.java create mode 100644 diameter-quarkus/diameter-server-quarkus/deployment/pom.xml create mode 100644 diameter-quarkus/diameter-server-quarkus/deployment/src/main/java/io/go/diameter/server/deployment/DiameterServerProcessor.java create mode 100644 diameter-quarkus/diameter-server-quarkus/pom.xml create mode 100644 diameter-quarkus/diameter-server-quarkus/runtime/pom.xml create mode 100644 diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/DiameterServer.java create mode 100644 diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/server/runtime/DiameterServerConfigProducer.java create mode 100644 diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/server/runtime/DiameterServerRecorder.java create mode 100644 diameter-quarkus/diameter-server-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml create mode 100644 diameter-quarkus/pom.xml create mode 100644 lombok.config diff --git a/diameter-quarkus/diameter-client-quarkus/deployment/pom.xml b/diameter-quarkus/diameter-client-quarkus/deployment/pom.xml new file mode 100644 index 000000000..4558bb864 --- /dev/null +++ b/diameter-quarkus/diameter-client-quarkus/deployment/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + io.go.diameter + diameter-client-quarkus-parent + 2.0.0 + ../pom.xml + + + diameter-client-quarkus-deployment + Diameter Client Quarkus Extension - Deployment + + + + io.quarkus + quarkus-arc-deployment + + + io.go.diameter + diameter-client-quarkus + ${project.version} + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/diameter-quarkus/diameter-client-quarkus/deployment/src/main/java/io/go/diameter/client/deployment/DiameterClientProcessor.java b/diameter-quarkus/diameter-client-quarkus/deployment/src/main/java/io/go/diameter/client/deployment/DiameterClientProcessor.java new file mode 100644 index 000000000..32b3af430 --- /dev/null +++ b/diameter-quarkus/diameter-client-quarkus/deployment/src/main/java/io/go/diameter/client/deployment/DiameterClientProcessor.java @@ -0,0 +1,91 @@ +package io.go.diameter.client.deployment; + +import io.go.diameter.DiameterClient; +import io.go.diameter.client.runtime.DiameterClientRecorder; +import io.go.diameter.config.DiameterClientConfig; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Default; +import org.jboss.jandex.DotName; +import org.jdiameter.api.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; + +class DiameterClientProcessor +{ + private static final Logger LOG = LoggerFactory.getLogger(DiameterClientProcessor.class); + private static final String DEFAULT_NAME = ""; + private static final String FEATURE = "diameter-client"; + private static final DotName CONFIGURATION_DOTNAME = DotName.createSimple("org.jdiameter.api.Configuration"); + + @BuildStep + FeatureBuildItem feature() + { + return new FeatureBuildItem(FEATURE); + } + + + @Record(RUNTIME_INIT) + @BuildStep + void generateDiameterClient(DiameterClientRecorder recorder, + DiameterClientConfig diameterConfig, + BuildProducer syntheticBeanBuildItemBuildProducer) + { + if (diameterConfig.defaultDiameterConfig() != null) { + //Define the default diameter config producer + syntheticBeanBuildItemBuildProducer + .produce(createSyntheticBean(DEFAULT_NAME, + false, + true) + .createWith(recorder.clientConfiguration(DEFAULT_NAME)) + .done()); + }//if + + + if (diameterConfig.namedDiameterConfigs() != null) { + for (String clientName : diameterConfig.namedDiameterConfigs().keySet()) { + //Define the named diameter config producer + syntheticBeanBuildItemBuildProducer + .produce(createSyntheticBean(clientName, + true, + false) + .createWith(recorder.clientConfiguration(clientName)) + .done()); + }//for + }//if + } + + private static SyntheticBeanBuildItem.ExtendedBeanConfigurator createSyntheticBean(String clientName, + boolean isNamedPersistenceUnit, + boolean defaultBean) + { + LOG.info("Creating Synthetic Bean for @DiameterClient(\"{}\")", clientName); + SyntheticBeanBuildItem.ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem + .configure(Configuration.class) + .scope(ApplicationScoped.class) + .unremovable() + .setRuntimeInit() + .addType(CONFIGURATION_DOTNAME); + + + if (defaultBean) { + configurator.defaultBean(); + } + + if (isNamedPersistenceUnit) { + configurator.addQualifier().annotation(DiameterClient.class).addValue("value", clientName).done(); + } + else { + configurator.addQualifier(Default.class); + configurator.addQualifier().annotation(DiameterClient.class).done(); + } + + return configurator; + } +} diff --git a/diameter-quarkus/diameter-client-quarkus/pom.xml b/diameter-quarkus/diameter-client-quarkus/pom.xml new file mode 100644 index 000000000..d462a98f7 --- /dev/null +++ b/diameter-quarkus/diameter-client-quarkus/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + + io.go.diameter + diameter-quarkus-parent + 2.0.0 + ../pom.xml + + + diameter-client-quarkus-parent + pom + Diameter Client Quarkus Extension - Parent + + + deployment + runtime + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-failsafe-plugin + ${failsafe-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + + + diff --git a/diameter-quarkus/diameter-client-quarkus/runtime/pom.xml b/diameter-quarkus/diameter-client-quarkus/runtime/pom.xml new file mode 100644 index 000000000..1bc966330 --- /dev/null +++ b/diameter-quarkus/diameter-client-quarkus/runtime/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + + io.go.diameter + diameter-client-quarkus-parent + 2.0.0 + ../pom.xml + + + diameter-client-quarkus + Diameter Client Quarkus Extension - Runtime + Diameter Client Quarkus Extension + + + + io.quarkus + quarkus-arc + + + org.projectlombok + lombok + + + io.go.diameter + jdiameter-api + + + io.go.diameter + jdiameter-impl + + + io.go.diameter + diameter-quarkus-config + 2.0.0 + compile + + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + io.go.diameter.client + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/diameter-quarkus/diameter-client-quarkus/runtime/src/main/java/io/go/diameter/DiameterClient.java b/diameter-quarkus/diameter-client-quarkus/runtime/src/main/java/io/go/diameter/DiameterClient.java new file mode 100644 index 000000000..3c078f95b --- /dev/null +++ b/diameter-quarkus/diameter-client-quarkus/runtime/src/main/java/io/go/diameter/DiameterClient.java @@ -0,0 +1,57 @@ +package io.go.diameter; + +import jakarta.enterprise.util.AnnotationLiteral; +import jakarta.inject.Qualifier; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.util.Objects; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({TYPE, FIELD, METHOD, PARAMETER}) +@Retention(RUNTIME) +@Documented +@Qualifier +public @interface DiameterClient +{ + String value() default ""; + + @SuppressWarnings("ClassExplicitlyAnnotation") + final class DiameterClientLiteral extends AnnotationLiteral implements DiameterClient + { + + private final String name; + + public DiameterClientLiteral(String name) + { + this.name = name; + } + + @Override + public String value() + { + return name; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (!(o instanceof DiameterClientLiteral that)) return false; + if (!super.equals(o)) return false; + + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() + { + int result = super.hashCode(); + result = 31 * result + (name != null ? name.hashCode() : 0); + return result; + } + } +} diff --git a/diameter-quarkus/diameter-client-quarkus/runtime/src/main/java/io/go/diameter/client/runtime/DiameterClientRecorder.java b/diameter-quarkus/diameter-client-quarkus/runtime/src/main/java/io/go/diameter/client/runtime/DiameterClientRecorder.java new file mode 100644 index 000000000..adb8e4516 --- /dev/null +++ b/diameter-quarkus/diameter-client-quarkus/runtime/src/main/java/io/go/diameter/client/runtime/DiameterClientRecorder.java @@ -0,0 +1,36 @@ +package io.go.diameter.client.runtime; + +import io.go.diameter.config.DiameterClientConfig; +import io.go.diameter.config.DiameterClientConfiguration; +import io.go.diameter.config.DiameterConfig; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.runtime.annotations.Recorder; +import io.smallrye.config.SmallRyeConfig; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.microprofile.config.ConfigProvider; +import org.jdiameter.api.Configuration; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +@Recorder +@Slf4j +public class DiameterClientRecorder +{ + private final Map configurationList = new ConcurrentHashMap<>(); + + public Function, Configuration> clientConfiguration(String clientName) + { + return context -> configurationList.computeIfAbsent(clientName, k -> { + SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class); + DiameterClientConfig client = config.getConfigMapping(DiameterClientConfig.class); + + DiameterConfig diameterConfig = client.getDiameterConfig(k); + if (diameterConfig == null) { + throw new IllegalArgumentException("No client configuration found for " + k); + } + return new DiameterClientConfiguration(diameterConfig); + }); + } +} diff --git a/diameter-quarkus/diameter-client-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/diameter-quarkus/diameter-client-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 000000000..63bdbe3c8 --- /dev/null +++ b/diameter-quarkus/diameter-client-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Go jDiameter +#description: Quarkus JDiameter Extension +metadata: +# keywords: +# - go-jdiameter +# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension +# categories: +# - "miscellaneous" +# status: "preview" diff --git a/diameter-quarkus/diameter-quarkus-config/pom.xml b/diameter-quarkus/diameter-quarkus-config/pom.xml new file mode 100644 index 000000000..e34fc44c9 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + io.go.diameter + diameter-quarkus-parent + 2.0.0 + ../pom.xml + + + diameter-quarkus-config + + + + io.smallrye.config + smallrye-config-core + compile + + + io.go.diameter + jdiameter-api + + + io.go.diameter + jdiameter-impl + + + io.quarkus + quarkus-core + + + + + + + io.smallrye + jandex-maven-plugin + ${maven-jandex-plugin.version} + + + make-index + + jandex + + + + + + + diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterClientConfig.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterClientConfig.java new file mode 100644 index 000000000..e7547777d --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterClientConfig.java @@ -0,0 +1,40 @@ +package io.go.diameter.config; + +import io.quarkus.runtime.annotations.ConfigDocMapKey; +import io.quarkus.runtime.annotations.ConfigDocSection; +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithParentName; + +import java.util.Map; + +@ConfigMapping(prefix = "diameter.client") +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface DiameterClientConfig +{ + /** + * The default diameter client config + */ + @ConfigDocSection + @WithParentName + DiameterConfig defaultDiameterConfig(); + + /** + * The defined named diameter client config + */ + @ConfigDocSection + @ConfigDocMapKey("name") + @WithParentName + Map namedDiameterConfigs(); + + + default DiameterConfig getDiameterConfig(String clientName) + { + if ("".equals(clientName)) { + return defaultDiameterConfig(); + }//if + + return namedDiameterConfigs().get(clientName); + }//getDiameterConfig +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterClientConfiguration.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterClientConfiguration.java new file mode 100644 index 000000000..d27bb47b4 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterClientConfiguration.java @@ -0,0 +1,280 @@ +package io.go.diameter.config; + +import io.go.diameter.config.mapping.*; +import org.jdiameter.api.Configuration; +import org.jdiameter.client.impl.helpers.AppConfiguration; +import org.jdiameter.client.impl.helpers.ExtensionPoint; +import org.jdiameter.client.impl.helpers.Ordinal; +import org.jdiameter.client.impl.helpers.Parameters; +import org.jdiameter.server.impl.helpers.EmptyConfiguration; + +import java.util.ArrayList; +import java.util.List; + +public class DiameterClientConfiguration extends EmptyConfiguration +{ + public DiameterClientConfiguration(DiameterConfig config) + { + addLocalPeer(config.localPeer()); + addParameters(config.parameter()); + addNetwork(config.network()); + addExtensions(config.extension()); + } + + protected void addLocalPeer(LocalPeer peerConfig) + { + add(Parameters.OwnDiameterURI, peerConfig.uri()); + add(Parameters.OwnRealm, peerConfig.realm()); + add(Parameters.OwnVendorID, peerConfig.vendorId()); + add(Parameters.OwnProductName, peerConfig.productName()); + add(Parameters.OwnFirmwareRevision, peerConfig.firmwareRevision()); + if (peerConfig.applications().isPresent()) { + addApplications(peerConfig.applications().get()); + } + } + + protected Configuration addApplicationID(ApplicationId applicationId) + { + if (applicationId != null) { + AppConfiguration e = EmptyConfiguration.getInstance(); + if (applicationId.vendorId().isPresent()) { + e.add(Parameters.VendorId, applicationId.vendorId().get()); + } + else { + e.add(Parameters.VendorId, 0L); + } + if (applicationId.authApplId().isPresent()) { + e.add(Parameters.AuthApplId, applicationId.authApplId().get()); + } + else { + e.add(Parameters.AuthApplId, 0L); + } + if (applicationId.acctApplId().isPresent()) { + e.add(Parameters.AcctApplId, applicationId.acctApplId().get()); + } + else { + e.add(Parameters.AcctApplId, 0L); + } + return e; + } + return null; + } + + protected void addApplications(List applications) + { + ArrayList items = new ArrayList<>(); + for (ApplicationId appId : applications) { + items.add(addApplicationID(appId)); + } + add(Parameters.ApplicationId, items.toArray(EMPTY_ARRAY)); + } + + protected void addParameters(Parameter parametersConfig) + { + if (parametersConfig.useUriAsFqdn().isPresent()) { + add(Parameters.UseUriAsFqdn, parametersConfig.useUriAsFqdn().get()); + } + else { + add(Parameters.UseUriAsFqdn, false); + } + if (parametersConfig.queueSize().isPresent()) { + add(Parameters.QueueSize, parametersConfig.queueSize().get()); + } + if (parametersConfig.messageTimeout().isPresent()) { + add(Parameters.MessageTimeOut, parametersConfig.messageTimeout().get()); + } + if (parametersConfig.stopTimeout().isPresent()) { + add(Parameters.StopTimeOut, parametersConfig.stopTimeout().get()); + } + if (parametersConfig.ceaTimeout().isPresent()) { + add(Parameters.CeaTimeOut, parametersConfig.ceaTimeout().get()); + } + if (parametersConfig.iacTimeout().isPresent()) { + add(Parameters.IacTimeOut, parametersConfig.iacTimeout().get()); + } + if (parametersConfig.dwaTimeout().isPresent()) { + add(Parameters.DwaTimeOut, parametersConfig.dwaTimeout().get()); + } + if (parametersConfig.dpaTimeout().isPresent()) { + add(Parameters.DpaTimeOut, parametersConfig.dpaTimeout().get()); + } + if (parametersConfig.recTimeout().isPresent()) { + add(Parameters.RecTimeOut, parametersConfig.recTimeout().get()); + } + if (parametersConfig.peerFSMThreadCount().isPresent()) { + add(Parameters.PeerFSMThreadCount, parametersConfig.peerFSMThreadCount().get()); + } + + if (parametersConfig.useVirtualThreads().isPresent()) { + add(Parameters.UseVirtualThreads, parametersConfig.useVirtualThreads().get()); + } + + if (parametersConfig.concurrent().isPresent()) { + addConcurrent(parametersConfig.concurrent().get()); + } + } + + protected AppConfiguration createConcurrentItem(String name, int size) + { + AppConfiguration cfg = EmptyConfiguration.getInstance(); + cfg.add(Parameters.ConcurrentEntityName, name); + cfg.add(Parameters.ConcurrentEntityPoolSize, size); + return cfg; + } + + protected void addConcurrent(Concurrent concurrent) + { + List items = new ArrayList<>(); + if (concurrent.applicationSession().isPresent()) { + items.add(createConcurrentItem("ApplicationSession", concurrent.applicationSession().get())); + } + + if (concurrent.duplicationMessageTimer().isPresent()) { + items.add(createConcurrentItem("DuplicateMessageTimer", concurrent.duplicationMessageTimer().get())); + } + + if (concurrent.processingMessageTimer().isPresent()) { + items.add(createConcurrentItem("ProcessingMessageTimer", concurrent.processingMessageTimer().get())); + } + + if (concurrent.connectionTimer().isPresent()) { + items.add(createConcurrentItem("ConnectionTimer", concurrent.connectionTimer().get())); + } + + if (concurrent.peerOverloadTimer().isPresent()) { + items.add(createConcurrentItem("PeerOverloadTimer", concurrent.peerOverloadTimer().get())); + } + + if (concurrent.redirectMessageTimer().isPresent()) { + items.add(createConcurrentItem("RedirectMessageTimer", concurrent.redirectMessageTimer().get())); + } + + if (concurrent.statisticTimer().isPresent()) { + items.add(createConcurrentItem("StatisticTimer", concurrent.statisticTimer().get())); + } + + if (concurrent.threadGroup().isPresent()) { + items.add(createConcurrentItem("ThreadGroup", concurrent.threadGroup().get())); + } + + add(Parameters.Concurrent, items.toArray(EMPTY_ARRAY)); + } + + protected void addNetwork(Network network) + { + addPeers(network.peers()); + addRealms(network.realms()); + } + + protected void addPeers(List peers) + { + ArrayList items = new ArrayList<>(); + peers.forEach(peer -> items.add(addPeer(peer))); + add(Parameters.PeerTable, items.toArray(EMPTY_ARRAY)); + } + + protected AppConfiguration addPeer(Peer peer) + { + AppConfiguration peerConfig = EmptyConfiguration.getInstance() + .add(Parameters.PeerRating, peer.rating()) + .add(Parameters.PeerName, peer.peerUri()); + + if (peer.ip().isPresent()) { + peerConfig.add(Parameters.PeerIp, peer.ip().get()); + } + + if (peer.portRange().isPresent()) { + peerConfig.add(Parameters.PeerLocalPortRange, peer.portRange().get()); + } + + return peerConfig; + } + + protected void addRealms(List realms) + { + ArrayList items = new ArrayList<>(); + realms.forEach(realm -> items.add(addRealm(realm))); + add(Parameters.RealmTable, items.toArray(EMPTY_ARRAY)); + } + + protected AppConfiguration buildRealm(Realm realm) + { + return EmptyConfiguration.getInstance(). + add(Parameters.ApplicationId, addApplicationID(realm.applicationId())); + } + + protected Configuration addRealm(Realm realm) + { + return EmptyConfiguration.getInstance().add(Parameters.RealmEntry, buildRealm(realm)); + } + + protected void addInternalExtension(Ordinal ep, String value) + { + Configuration[] extensionConfs = this.getChildren(org.jdiameter.client.impl.helpers.Parameters.Extensions.ordinal()); + AppConfiguration internalExtensions = (AppConfiguration) extensionConfs[org.jdiameter.client.impl.helpers.ExtensionPoint.Internal.id()]; + internalExtensions.add(ep, value); + } + + protected void addExtensions(Extension extension) + { + if (extension.metaData().isPresent()) { + addInternalExtension(ExtensionPoint.InternalMetaData, extension.metaData().get()); + } + + if (extension.messageParser().isPresent()) { + addInternalExtension(ExtensionPoint.InternalMessageParser, extension.messageParser().get()); + } + + if (extension.elementParser().isPresent()) { + addInternalExtension(ExtensionPoint.InternalElementParser, extension.elementParser().get()); + } + + if (extension.transportFactory().isPresent()) { + addInternalExtension(ExtensionPoint.InternalTransportFactory, extension.transportFactory().get()); + } + + if (extension.connection().isPresent()) { + addInternalExtension(ExtensionPoint.InternalConnectionClass, extension.connection().get()); + } + + if (extension.peerFsmFactory().isPresent()) { + addInternalExtension(ExtensionPoint.InternalPeerFsmFactory, extension.peerFsmFactory().get()); + } + + if (extension.sessionFactory().isPresent()) { + addInternalExtension(ExtensionPoint.InternalSessionFactory, extension.sessionFactory().get()); + } + + if (extension.routerEngine().isPresent()) { + addInternalExtension(ExtensionPoint.InternalRouterEngine, extension.routerEngine().get()); + } + + if (extension.statisticFactory().isPresent()) { + addInternalExtension(ExtensionPoint.InternalStatisticFactory, extension.statisticFactory().get()); + } + + if (extension.realmController().isPresent()) { + addInternalExtension(ExtensionPoint.InternalRealmController, extension.realmController().get()); + } + + if (extension.agentRedirect().isPresent()) { + addInternalExtension(ExtensionPoint.InternalAgentRedirect, extension.agentRedirect().get()); + } + + if (extension.agentConfiguration().isPresent()) { + addInternalExtension(ExtensionPoint.InternalAgentConfiguration, extension.agentConfiguration().get()); + } + if (extension.agentProxy().isPresent()) { + addInternalExtension(ExtensionPoint.InternalAgentProxy, extension.agentProxy().get()); + } + if (extension.sessionDatasource().isPresent()) { + addInternalExtension(ExtensionPoint.InternalSessionDatasource, extension.agentRedirect().get()); + } + if (extension.timerFacility().isPresent()) { + addInternalExtension(ExtensionPoint.InternalTimerFacility, extension.timerFacility().get()); + } + if (extension.peerController().isPresent()) { + addInternalExtension(ExtensionPoint.InternalPeerController, extension.peerController().get()); + } + } +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterConfig.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterConfig.java new file mode 100644 index 000000000..418a253f9 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterConfig.java @@ -0,0 +1,39 @@ +package io.go.diameter.config; + +import io.go.diameter.config.mapping.Extension; +import io.go.diameter.config.mapping.LocalPeer; +import io.go.diameter.config.mapping.Network; +import io.go.diameter.config.mapping.Parameter; +import io.quarkus.runtime.annotations.ConfigDocSection; +import io.smallrye.config.WithName; + +public interface DiameterConfig +{ + /** + * The localPeer element contains parameters that affect the local Diameter peer. + */ + @WithName("local-peer") + @ConfigDocSection + LocalPeer localPeer(); + + /** + * The Parameters element contains elements that specify parameters for the Diameter stack. + */ + @WithName("parameter") + @ConfigDocSection + Parameter parameter(); + + /** + * The Network< element contains elements that specify parameters for external peers. + */ + @WithName("network") + @ConfigDocSection + Network network(); + + /** + * The extensions elements contains elements that override existing components in the Diameter stack. + */ + @WithName("extensions") + @ConfigDocSection + Extension extension(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterServerConfig.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterServerConfig.java new file mode 100644 index 000000000..5eebb075f --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterServerConfig.java @@ -0,0 +1,11 @@ +package io.go.diameter.config; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; + +@ConfigMapping(prefix = "diameter.server") +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface DiameterServerConfig extends DiameterConfig +{ +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterServerConfiguration.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterServerConfiguration.java new file mode 100644 index 000000000..65ee12275 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/DiameterServerConfiguration.java @@ -0,0 +1,132 @@ +package io.go.diameter.config; + +import io.go.diameter.config.mapping.*; +import org.jdiameter.api.Configuration; +import org.jdiameter.client.impl.helpers.AppConfiguration; +import org.jdiameter.server.impl.helpers.EmptyConfiguration; +import org.jdiameter.server.impl.helpers.ExtensionPoint; +import org.jdiameter.server.impl.helpers.Parameters; + +import java.util.ArrayList; +import java.util.List; + +public class DiameterServerConfiguration extends DiameterClientConfiguration +{ + public DiameterServerConfiguration(DiameterConfig config) + { + super(config); + } + + @Override + protected void addLocalPeer(LocalPeer peerConfig) + { + super.addLocalPeer(peerConfig); + + addIPAddresses(peerConfig.ipAddresses()); + + if (peerConfig.overloadMonitors().isPresent()) { + addOverloadMonitor(peerConfig.overloadMonitors().get()); + } + } + + protected void addOverloadMonitor(List entries) + { + ArrayList items = new ArrayList<>(); + entries.forEach(entry -> items.add(addOverloadMonitorItem(entry))); + add(Parameters.OverloadMonitor, items.toArray(EMPTY_ARRAY)); + } + + protected Configuration addOverloadMonitorItem(OverloadMonitor entry) + { + return EmptyConfiguration.getInstance() + .add(Parameters.OverloadEntryIndex, entry.index()) + .add(Parameters.OverloadEntrylowThreshold, entry.lowThreshold()) + .add(Parameters.OverloadEntryhighThreshold, entry.highThreshold()) + .add(Parameters.ApplicationId, addApplicationID(entry.applicationId())); + } + + protected void addIPAddresses(List ipAddresses) + { + ArrayList items = new ArrayList<>(); + ipAddresses.forEach(ip -> items.add(EmptyConfiguration.getInstance().add(Parameters.OwnIPAddress, ip))); + add(Parameters.OwnIPAddresses, items.toArray(EMPTY_ARRAY)); + } + + @Override + protected AppConfiguration addPeer(Peer peer) + { + AppConfiguration peerConfig = super.addPeer(peer); + + peerConfig.add(Parameters.PeerAttemptConnection, peer.attemptConnect()); + + return peerConfig; + } + + @Override + protected AppConfiguration buildRealm(Realm realm) + { + AppConfiguration realmEntry = super.buildRealm(realm); + realmEntry.add(Parameters.RealmName, realm.realmName()) + .add(Parameters.RealmHosts, realm.peers()) + .add(Parameters.RealmLocalAction, realm.localAction().name()) + .add(Parameters.RealmEntryIsDynamic, realm.dynamic()) + .add(Parameters.RealmEntryExpTime, realm.expTime()); + return realmEntry; + } + + @Override + protected void addParameters(Parameter parametersConfig) + { + super.addParameters(parametersConfig); + + if (parametersConfig.acceptUndefinedPeer().isPresent()) { + add(Parameters.AcceptUndefinedPeer, parametersConfig.acceptUndefinedPeer().get()); + } + else { + add(Parameters.AcceptUndefinedPeer, false); + } + + if (parametersConfig.duplicateTimer().isPresent()) { + add(Parameters.DuplicateTimer, parametersConfig.duplicateTimer().get()); + } + else { + add(Parameters.DuplicateTimer, 240000L); + } + + if (parametersConfig.duplicateProtection().isPresent()) { + add(Parameters.DuplicateProtection, parametersConfig.duplicateProtection().get()); + } + else { + add(Parameters.DuplicateProtection, false); + } + + if (parametersConfig.duplicateSize().isPresent()) { + add(Parameters.DuplicateSize, parametersConfig.duplicateSize().get()); + } + else { + add(Parameters.DuplicateSize, 5000L); + } + + if (parametersConfig.bindDelay().isPresent()) { + add(Parameters.BindDelay, parametersConfig.bindDelay().get()); + } + } + + @Override + protected void addExtensions(Extension extension) + { + super.addExtensions(extension); + + if (extension.networkGuard().isPresent()) { + addInternalExtension(ExtensionPoint.InternalNetworkGuard, extension.networkGuard().get()); + } + + if (extension.network().isPresent()) { + addInternalExtension(ExtensionPoint.InternalNetWork, extension.network().get()); + } + + if (extension.overloadManager().isPresent()) { + addInternalExtension(ExtensionPoint.InternalOverloadManager, extension.overloadManager().get()); + } + } +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/ApplicationId.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/ApplicationId.java new file mode 100644 index 000000000..c2f74d239 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/ApplicationId.java @@ -0,0 +1,30 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; + +import java.util.Optional; + +public interface ApplicationId +{ + /** + * Specifies the vendor ID for application definition. + */ + @WithDefault("0") + @WithName("vendor-id") + Optional vendorId(); + + /** + * The Authentication Application ID for application definition. + */ + @WithDefault("0") + @WithName("auth-appl-id") + Optional authApplId(); + + /** + * The Account Application ID for application definition. + */ + @WithDefault("0") + @WithName("acct-appl-id") + Optional acctApplId(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Concurrent.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Concurrent.java new file mode 100644 index 000000000..b4c755172 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Concurrent.java @@ -0,0 +1,57 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithName; + +import java.util.Optional; + +public interface Concurrent +{ + /** + * Determines the maximum thread count in other entities. + */ + @WithName("thread-group") + Optional threadGroup(); + + /** + * Determines the thread count for message processing tasks. + */ + @WithName("processing-message-timer") + Optional processingMessageTimer(); + + /** + * Specifies the thread pool for identifying duplicate messages. + */ + @WithName("duplication-message-timer") + Optional duplicationMessageTimer(); + + /** + * Specifies the thread pool for redirecting messages that do not need any further processing. + */ + @WithName("redirect-message-timer") + Optional redirectMessageTimer(); + + /** + * Determines the thread pool for managing the overload monitor. + */ + @WithName("peer-overload-timer") + Optional peerOverloadTimer(); + + /** + * Determines the thread pool for managing tasks regarding peer connection FSM. + */ + @WithName("connection-timer") + Optional connectionTimer(); + + /** + * Determines the thread pool for statistic gathering tasks. + */ + @WithName("statistic-timer") + Optional statisticTimer(); + + /** + * Determines the thread pool for managing the invocation of application session FSMs, + * which will invoke listeners. + */ + @WithName("application-session") + Optional applicationSession(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Extension.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Extension.java new file mode 100644 index 000000000..d19ddbae2 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Extension.java @@ -0,0 +1,140 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithName; + +import java.util.Optional; + +public interface Extension +{ + /** + * The MetaData extension + */ + @WithName("metadata") + Optional metaData(); + + /** + * The MetaData extension + */ + @WithName("message-parser") + Optional messageParser(); + + /** + * The MetaData extension + */ + @WithName("element-parser") + Optional elementParser(); + + /** + * The MetaData extension + */ + @WithName("router-engine") + Optional routerEngine(); + + /** + * The MetaData extension + */ + @WithName("peer-controller") + Optional peerController(); + + /** + * The Realm Controller extension + */ + @WithName("realm-controller") + Optional realmController(); + + /** + * The Session Factory extension + */ + @WithName("session-factory") + Optional sessionFactory(); + + /** + * The Transport Factory extension + */ + @WithName("transport-factory") + Optional transportFactory(); + + /** + * The Connection extension + */ + @WithName("connection") + Optional connection(); + + /** + * The Network Guard extension + */ + @WithName("network-guard") + Optional networkGuard(); + + /** + * The Peer Fsm Factory extension + */ + @WithName("peer-fsm-factory") + Optional peerFsmFactory(); + + /** + * The Statistic Factory extension + */ + @WithName("statistic-factory") + Optional statisticFactory(); + + /** + * The Concurrent Factory extension + */ + @WithName("concurrent-factory") + Optional concurrentFactory(); + + /** + * The Concurrent Entity Factory extension + */ + @WithName("concurrent-entity-factory") + Optional concurrentEntityFactory(); + + /** + * The Statistic Processor extension + */ + @WithName("statistic-processor") + Optional statisticProcessor(); + + /** + * The NetWork extension + */ + @WithName("network") + Optional network(); + + /** + * The Session Datasource extension + */ + @WithName("session-datasource") + Optional sessionDatasource(); + + /** + * The Timer Facility extension + */ + @WithName("timer-facility") + Optional timerFacility(); + + /** + * The Agent Redirect extension + */ + @WithName("agent-redirect") + Optional agentRedirect(); + + /** + * The Agent Configuration extension + */ + @WithName("agent-configuration") + Optional agentConfiguration(); + + /** + * The Agen tProxy extension + */ + @WithName("agent-proxy") + Optional agentProxy(); + + /** + * The Overload Manager extension + */ + @WithName("overload-manager") + Optional overloadManager(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/LocalPeer.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/LocalPeer.java new file mode 100644 index 000000000..e37886880 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/LocalPeer.java @@ -0,0 +1,63 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; + +import java.util.List; +import java.util.Optional; + +public interface LocalPeer +{ + /** + * Specifies the URI for the local peer. The URI has the following format: "aaa://FQDN:port". + */ + @WithDefault("aaa://localhost:1812") + @WithName("uri") + String uri(); + + /** + * Contains one or more valid IP address for the local peer.` + */ + @WithName("ip-addresses") + List ipAddresses(); + + /** + * Specifies the realm of the local peer. + */ + @WithName("realm") + String realm(); + + /** + * Specifies the name of the local peer product + */ + @WithDefault("Go Diameter") + @WithName("product-name") + String productName(); + + /** + * Specifies the version of the firmware. + */ + @WithDefault("1") + @WithName("firmware-revision") + long firmwareRevision(); + + /** + * Specifies a numeric identifier that corresponds to the vendor ID allocated by IANA. + */ + @WithDefault("0") + @WithName("vendor-id") + long vendorId(); + + /** + * Contains a list of default supported applications. + */ + @WithName("applications") + Optional> applications(); + + /** + * Optional parent element containing child elements that specify settings + * relating to the Overload Monitor. + */ + @WithName("overload-monitors") + Optional> overloadMonitors(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Network.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Network.java new file mode 100644 index 000000000..82d05b98d --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Network.java @@ -0,0 +1,20 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithName; + +import java.util.List; + +public interface Network +{ + /** + * List of external peers and the way they connect. + */ + @WithName("peers") + List peers(); + + /** + * List of all realms that connect into the Diameter network. + */ + @WithName("realms") + List realms(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/OverloadMonitor.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/OverloadMonitor.java new file mode 100644 index 000000000..90690e01f --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/OverloadMonitor.java @@ -0,0 +1,30 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithName; + +public interface OverloadMonitor +{ + /** + * Defines the index of this overload monitor, so priorities/orders can be specified. + */ + @WithName("index") + int index(); + + /** + * The low threshold for activation of the overload monitor. + */ + @WithName("low-threshold") + double lowThreshold(); + + /** + * The high threshold for activation of the overload monitor. + */ + @WithName("high-threshold") + double highThreshold(); + + /** + * The application that is overloaded + */ + @WithName("application-id") + ApplicationId applicationId(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Parameter.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Parameter.java new file mode 100644 index 000000000..b68565238 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Parameter.java @@ -0,0 +1,136 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; + +import java.util.Optional; + +public interface Parameter +{ + /** + * Specifies whether the stack will accept connections from undefined peers. + * The default value is `false` + */ + @WithDefault("false") + @WithName("accept-undefined-peer") + Optional acceptUndefinedPeer(); + + /** + * Specifies whether duplicate message protection is enabled. + * The default value is `false`. + */ + @WithDefault("false") + @WithName("duplicate-protection") + Optional duplicateProtection(); + + /** + * Determines whether the URI should be used as FQDN. + * If it is set to `true`, the stack expects the destination/origin host to be in the + * format of "aaa://isdn.domain.com:3868" rather than the normal "isdn.domain.com". + * The default value is `false`. + */ + @WithDefault("false") + @WithName("use-uri-as-fqdn") + Optional useUriAsFqdn(); + + /** + * Specifies the time each duplicate message is valid for (in extreme cases, it can + * live up to 2 * DuplicateTimer - 1 milliseconds). + * The default, minimum value is `240000` (4 minutes in milliseconds). + */ + @WithDefault("240000") + @WithName("duplicate-timer") + Optional duplicateTimer(); + + /** + * Specifies the number of requests stored for duplicate protection. + * The default value is `5000`. + */ + @WithDefault("5000") + @WithName("duplicate-size") + Optional duplicateSize(); + + /** + * Determines how many tasks the peer state machine can have before rejecting the next task. + * This queue contains FSM events and messaging + */ + @WithName("queue-size") + Optional queueSize(); + + /** + * Determines the timeout for messages other than protocol FSM messages. + * The delay is in milliseconds. + */ + @WithName("message-timeout") + Optional messageTimeout(); + + /** + * Determines how long the stack waits for all resources to stop. + * The delays are in milliseconds. + */ + @WithName("stop-timeout") + Optional stopTimeout(); + + /** + * Determines how long it takes for CER/CEA exchanges to timeout if there is no response. + * The delays are in milliseconds. + */ + @WithName("cea-timeout") + Optional ceaTimeout(); + + /** + * Determines how long the stack waits to retry the communication with a peer that + * has stopped answering DWR messages. + * The delay is in milliseconds. + */ + @WithName("iac-timeout") + Optional iacTimeout(); + + /** + * Determines how long it takes for a DWR/DWA exchange to timeout if there is no response. + * The delay is in milliseconds. + */ + @WithName("dwa-timeout") + Optional dwaTimeout(); + + /** + * Determines how long it takes for a DPR/DPA exchange to timeout if there is no response. + * The delay is in milliseconds. + */ + @WithName("dpa-timeout") + Optional dpaTimeout(); + + /** + * Determines how long it takes for the reconnection procedure to timeout. + * The delay is in milliseconds. + */ + @WithName("rec-timeout") + Optional recTimeout(); + + /** + * Determines the number of threads for handling events in the Peer FSM. + */ + @WithName("peer-fsm-thread-count") + Optional peerFSMThreadCount(); + + /** + * Determines a delay before binding. + * The delay is in milliseconds. + */ + @WithName("bind-delay") + Optional bindDelay(); + + /** + * Specifies whether the stack should use virtual threads + * The default value is `false` + */ + @WithDefault("false") + @WithName("use-virtual-threads") + Optional useVirtualThreads(); + + /** + * Controls the thread pool sizes for different aspects of the stack. + */ + @WithName("concurrent") + Optional concurrent(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Peer.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Peer.java new file mode 100644 index 000000000..ca56bcc9d --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Peer.java @@ -0,0 +1,42 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; + +import java.util.Optional; + +public interface Peer +{ + /** + * Specifies the name of the peer in the form of a URI. + * The structure is "aaa://[fqdn|ip]:port" (for example, "aaa://192.168.1.1:3868"). + */ + @WithName("peer-uri") + String peerUri(); + + /** + * Specifies the rating of this peer in order to achieve peer priorities/sorting. + */ + @WithDefault("1") + @WithName("rating") + int rating(); + + /** + * Specifies the actual ip for the peer-uri, for example 192.168.1.1 + */ + @WithName("ip") + Optional ip(); + + /** + * Specifies a port range to accept connection override the port number in peer-uri + */ + @WithName("port-range") + Optional portRange(); + + /** + * Determines if the stack should try to connect to this peer. + */ + @WithDefault("false") + @WithName("attempt-connect") + Boolean attemptConnect(); +} diff --git a/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Realm.java b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Realm.java new file mode 100644 index 000000000..77c0c6459 --- /dev/null +++ b/diameter-quarkus/diameter-quarkus-config/src/main/java/io/go/diameter/config/mapping/Realm.java @@ -0,0 +1,50 @@ +package io.go.diameter.config.mapping; + +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; +import org.jdiameter.api.LocalAction; + +public interface Realm +{ + /** + * Contains attributes and elements that describe different realms configured for the Core. + */ + @WithName("realm-name") + String realmName(); + + /** + * Comma separated list of peers. Each peer is represented by an IP Address or FQDN. + */ + @WithName("peers") + String peers(); + + /** + * Determines the action the Local Peer will play on the specified realm: Act as a LOCAL peer. + */ + @WithDefault("LOCAL") + @WithName("local-action") + LocalAction localAction(); + + /** + * Specifies if this realm is dynamic. That is, peers that + * connect to peers with this realm name will be added to the realm peer + * list if not present already. + */ + @WithDefault("false") + @WithName("dynamic") + Boolean dynamic(); + + /** + * The time before a peer belonging to this realm is removed if no connection is available. + * The time is in seconds. + */ + @WithDefault("1") + @WithName("exp-time") + long expTime(); + + /** + * The applications supported. + */ + @WithName("application-id") + ApplicationId applicationId(); +} diff --git a/diameter-quarkus/diameter-server-quarkus/deployment/pom.xml b/diameter-quarkus/diameter-server-quarkus/deployment/pom.xml new file mode 100644 index 000000000..c4a353320 --- /dev/null +++ b/diameter-quarkus/diameter-server-quarkus/deployment/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + io.go.diameter + diameter-server-quarkus-parent + 2.0.0 + ../pom.xml + + + diameter-server-quarkus-deployment + Diameter Server Quarkus Extension - Deployment + + + + io.quarkus + quarkus-arc-deployment + + + io.go.diameter + diameter-server-quarkus + ${project.version} + + + io.quarkus + quarkus-junit5-internal + test + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + + diff --git a/diameter-quarkus/diameter-server-quarkus/deployment/src/main/java/io/go/diameter/server/deployment/DiameterServerProcessor.java b/diameter-quarkus/diameter-server-quarkus/deployment/src/main/java/io/go/diameter/server/deployment/DiameterServerProcessor.java new file mode 100644 index 000000000..5c0105b6b --- /dev/null +++ b/diameter-quarkus/diameter-server-quarkus/deployment/src/main/java/io/go/diameter/server/deployment/DiameterServerProcessor.java @@ -0,0 +1,50 @@ +package io.go.diameter.server.deployment; + +import io.go.diameter.DiameterServer; +import io.go.diameter.server.runtime.DiameterServerRecorder; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Default; +import org.jboss.jandex.DotName; +import org.jdiameter.api.Configuration; + +import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT; + +class DiameterServerProcessor +{ + private static final DotName CONFIGURATION_DOTNAME = DotName.createSimple("org.jdiameter.api.Configuration"); + private static final String FEATURE = "diameter-server"; + + @BuildStep + FeatureBuildItem feature() + { + return new FeatureBuildItem(FEATURE); + } + + @Record(RUNTIME_INIT) + @BuildStep + void generateDiameterServer(DiameterServerRecorder recorder, + BuildProducer syntheticBeanBuildItemBuildProducer) + { + SyntheticBeanBuildItem.ExtendedBeanConfigurator configurator = SyntheticBeanBuildItem + .configure(Configuration.class) + .scope(ApplicationScoped.class) + .unremovable() + .setRuntimeInit() + .addType(CONFIGURATION_DOTNAME) + .defaultBean() + .addQualifier(Default.class) + .addQualifier().annotation(DiameterServer.class) + .done(); + + //Define the diameter server config producer + syntheticBeanBuildItemBuildProducer + .produce(configurator + .createWith(recorder.serverConfiguration()) + .done()); + } +} diff --git a/diameter-quarkus/diameter-server-quarkus/pom.xml b/diameter-quarkus/diameter-server-quarkus/pom.xml new file mode 100644 index 000000000..be55c7441 --- /dev/null +++ b/diameter-quarkus/diameter-server-quarkus/pom.xml @@ -0,0 +1,64 @@ + + + 4.0.0 + + + io.go.diameter + diameter-quarkus-parent + 2.0.0 + ../pom.xml + + + diameter-server-quarkus-parent + pom + Diameter Server Quarkus Extension - Parent + + + deployment + runtime + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-failsafe-plugin + ${failsafe-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + + + diff --git a/diameter-quarkus/diameter-server-quarkus/runtime/pom.xml b/diameter-quarkus/diameter-server-quarkus/runtime/pom.xml new file mode 100644 index 000000000..1c1a2b776 --- /dev/null +++ b/diameter-quarkus/diameter-server-quarkus/runtime/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + + io.go.diameter + diameter-server-quarkus-parent + 2.0.0 + ../pom.xml + + + diameter-server-quarkus + Diameter Server Quarkus Extension - Runtime + Diameter Server Quarkus Extension + + + + io.quarkus + quarkus-arc + + + org.projectlombok + lombok + + + io.go.diameter + jdiameter-api + + + io.go.diameter + jdiameter-impl + + + io.go.diameter + diameter-quarkus-config + + + + + + + io.quarkus + quarkus-extension-maven-plugin + ${quarkus.version} + + + compile + + extension-descriptor + + + ${project.groupId}:${project.artifactId}-deployment:${project.version} + + + io.go.diameter.server + + + + + + + maven-compiler-plugin + + + + io.quarkus + quarkus-extension-processor + ${quarkus.version} + + + + + + io.smallrye + jandex-maven-plugin + ${maven-jandex-plugin.version} + + + make-index + + jandex + + + + + + + diff --git a/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/DiameterServer.java b/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/DiameterServer.java new file mode 100644 index 000000000..bc9fc0adc --- /dev/null +++ b/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/DiameterServer.java @@ -0,0 +1,38 @@ +package io.go.diameter; + +import jakarta.enterprise.util.AnnotationLiteral; +import jakarta.inject.Qualifier; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({TYPE, FIELD, METHOD, PARAMETER}) +@Retention(RUNTIME) +@Documented +@Qualifier +public @interface DiameterServer +{ + @SuppressWarnings("ClassExplicitlyAnnotation") + final class DiameterServerLiteral extends AnnotationLiteral implements DiameterServer + { + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (!(o instanceof DiameterServerLiteral)) return false; + return super.equals(o); + } + + @Override + public int hashCode() + { + int result = super.hashCode(); + result = 31 * result; + return result; + } + } +} diff --git a/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/server/runtime/DiameterServerConfigProducer.java b/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/server/runtime/DiameterServerConfigProducer.java new file mode 100644 index 000000000..f446754cc --- /dev/null +++ b/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/server/runtime/DiameterServerConfigProducer.java @@ -0,0 +1,23 @@ +package io.go.diameter.server.runtime; + +import io.go.diameter.DiameterServer; +import io.go.diameter.config.DiameterServerConfig; +import io.go.diameter.config.DiameterServerConfiguration; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; +import org.jdiameter.api.Configuration; + +@ApplicationScoped +public class DiameterServerConfigProducer +{ + @Inject + DiameterServerConfig config; + + @DiameterServer + @Produces + public Configuration getConfiguration() + { + return new DiameterServerConfiguration(config); + } +} diff --git a/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/server/runtime/DiameterServerRecorder.java b/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/server/runtime/DiameterServerRecorder.java new file mode 100644 index 000000000..bd54381da --- /dev/null +++ b/diameter-quarkus/diameter-server-quarkus/runtime/src/main/java/io/go/diameter/server/runtime/DiameterServerRecorder.java @@ -0,0 +1,37 @@ +package io.go.diameter.server.runtime; + +import io.go.diameter.config.DiameterServerConfig; +import io.go.diameter.config.DiameterServerConfiguration; +import io.quarkus.arc.SyntheticCreationalContext; +import io.quarkus.runtime.annotations.Recorder; +import jakarta.inject.Inject; +import org.jdiameter.api.Configuration; + +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Function; + +@Recorder +public class DiameterServerRecorder +{ + @Inject + DiameterServerConfig config; + + private final ReentrantLock lock = new ReentrantLock(); + private DiameterServerConfiguration serverConfiguration; + + public Function, Configuration> serverConfiguration() + { + return context -> { + lock.lock(); + try { + if (serverConfiguration == null) { + serverConfiguration = new DiameterServerConfiguration(config); + } + return serverConfiguration; + } + finally { + lock.unlock(); + } + }; + } +} diff --git a/diameter-quarkus/diameter-server-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/diameter-quarkus/diameter-server-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 000000000..63bdbe3c8 --- /dev/null +++ b/diameter-quarkus/diameter-server-quarkus/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,9 @@ +name: Go jDiameter +#description: Quarkus JDiameter Extension +metadata: +# keywords: +# - go-jdiameter +# guide: ... # To create and publish this guide, see https://github.com/quarkiverse/quarkiverse/wiki#documenting-your-extension +# categories: +# - "miscellaneous" +# status: "preview" diff --git a/diameter-quarkus/pom.xml b/diameter-quarkus/pom.xml new file mode 100644 index 000000000..cc8ad919b --- /dev/null +++ b/diameter-quarkus/pom.xml @@ -0,0 +1,103 @@ + + + 4.0.0 + + + io.go.diameter + diameter-parent + 2.0.0 + ../pom.xml + + + diameter-quarkus-parent + pom + Diameter Quarkus Extension - Parent + + + diameter-quarkus-config + diameter-server-quarkus + diameter-client-quarkus + + + + 3.13.0 + ${surefire-plugin.version} + 17 + UTF-8 + UTF-8 + 3.12.0 + 3.2.5 + 3.1.1 + + + + + + io.quarkus + quarkus-bom + ${quarkus.version} + pom + import + + + io.go.diameter + jdiameter-api + ${project.version} + + + io.go.diameter + jdiameter-impl + ${project.version} + + + io.go.diameter + diameter-quarkus-config + ${project.version} + + + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-failsafe-plugin + ${failsafe-plugin.version} + + + org.jboss.logmanager.LogManager + ${maven.home} + ${settings.localRepository} + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + -parameters + + + + + + + diff --git a/examples/guide1/src/main/java/org/example/server/ExampleServer.java b/examples/guide1/src/main/java/org/example/server/ExampleServer.java index 119b40581..4893395c6 100644 --- a/examples/guide1/src/main/java/org/example/server/ExampleServer.java +++ b/examples/guide1/src/main/java/org/example/server/ExampleServer.java @@ -48,350 +48,370 @@ /** * @author baranowb - * */ -public class ExampleServer implements NetworkReqListener { - private static final Logger log = Logger.getLogger(ExampleServer.class); - static{ - - configLog4j(); - -} - -private static void configLog4j() { - InputStream inStreamLog4j = ExampleServer.class.getClassLoader().getResourceAsStream("log4j.properties"); - Properties propertiesLog4j = new Properties(); - try { - propertiesLog4j.load(inStreamLog4j); - PropertyConfigurator.configure(propertiesLog4j); - } catch (Exception e) { - e.printStackTrace(); - } - - log.debug("log4j configured"); - -} - private static final String configFile = "org/example/server/server-jdiameter-config.xml"; - private static final String dictionaryFile = "org/example/client/dictionary.xml"; - private static final String realmName = "exchange.example.org"; - // Defs for our app - private static final int commandCode = 686; - private static final long vendorID = 66666; - private static final long applicationID = 33333; - private ApplicationId authAppId = ApplicationId.createByAuthAppId(applicationID);; - private static final int exchangeTypeCode = 888; - private static final int exchangeDataCode = 999; - // enum values for Exchange-Type AVP - private static final int EXCHANGE_TYPE_INITIAL = 0; - private static final int EXCHANGE_TYPE_INTERMEDIATE = 1; - private static final int EXCHANGE_TYPE_TERMINATING = 2; - - private static final String[] TO_RECEIVE = new String[] { "I want to get 3 answers", "This is second message", "Bye bye" }; - private AvpDictionary dictionary = AvpDictionary.INSTANCE; - private Stack stack; - private SessionFactory factory; - - // //////////////////////////////////////// - // Objects which will be used in action // - // //////////////////////////////////////// - private Session session; - private int toReceiveIndex = 0; - private boolean finished = false; - - private void initStack() { - if (log.isInfoEnabled()) { - log.info("Initializing Stack..."); - } - InputStream is = null; - try { - dictionary.parseDictionary(this.getClass().getClassLoader().getResourceAsStream(dictionaryFile)); - log.info("AVP Dictionary successfully parsed."); - this.stack = new StackImpl(); - - is = this.getClass().getClassLoader().getResourceAsStream(configFile); - - Configuration config = new XMLConfiguration(is); - factory = stack.init(config); - if (log.isInfoEnabled()) { - log.info("Stack Configuration successfully loaded."); - } - - Set appIds = stack.getMetaData().getLocalPeer().getCommonApplications(); - - log.info("Diameter Stack :: Supporting " + appIds.size() + " applications."); - for (org.jdiameter.api.ApplicationId x : appIds) { - log.info("Diameter Stack :: Common :: " + x); - } - is.close(); - Network network = stack.unwrap(Network.class); - network.addNetworkReqListener(this, this.authAppId); - } catch (Exception e) { - e.printStackTrace(); - if (this.stack != null) { - this.stack.destroy(); - } - - if (is != null) { - try { - is.close(); - } catch (IOException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - } - return; - } - - MetaData metaData = stack.getMetaData(); - if (metaData.getStackType() != StackType.TYPE_SERVER || metaData.getMinorVersion() <= 0) { - stack.destroy(); - if (log.isEnabledFor(org.apache.log4j.Level.ERROR)) { - log.error("Incorrect driver"); - } - return; - } - - try { - if (log.isInfoEnabled()) { - log.info("Starting stack"); - } - stack.start(); - if (log.isInfoEnabled()) { - log.info("Stack is running."); - } - } catch (Exception e) { - e.printStackTrace(); - stack.destroy(); - return; - } - if (log.isInfoEnabled()) { - log.info("Stack initialization successfully completed."); - } - } - - private void dumpMessage(Message message, boolean sending) { - if (log.isInfoEnabled()) { - log.info((sending?"Sending ":"Received ") + (message.isRequest() ? "Request: " : "Answer: ") + message.getCommandCode() + "\nE2E:" - + message.getEndToEndIdentifier() + "\nHBH:" + message.getHopByHopIdentifier() + "\nAppID:" + message.getApplicationId()); - log.info("AVPS["+message.getAvps().size()+"]: \n"); - try { - printAvps(message.getAvps()); - } catch (AvpDataException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - private void printAvps(AvpSet avpSet) throws AvpDataException { - printAvpsAux(avpSet, 0); - } - - /** - * Prints the AVPs present in an AvpSet with a specified 'tab' level - * - * @param avpSet - * the AvpSet containing the AVPs to be printed - * @param level - * an int representing the number of 'tabs' to make a pretty - * print - * @throws AvpDataException - */ - private void printAvpsAux(AvpSet avpSet, int level) throws AvpDataException { - String prefix = " ".substring(0, level * 2); - - for (Avp avp : avpSet) { - AvpRepresentation avpRep = AvpDictionary.INSTANCE.getAvp(avp.getCode(), avp.getVendorId()); - - if (avpRep != null && avpRep.getType().equals("Grouped")) { - log.info(prefix + ""); - printAvpsAux(avp.getGrouped(), level + 1); - log.info(prefix + ""); - } else if (avpRep != null) { - String value = ""; - - if (avpRep.getType().equals("Integer32")) - value = String.valueOf(avp.getInteger32()); - else if (avpRep.getType().equals("Integer64") || avpRep.getType().equals("Unsigned64")) - value = String.valueOf(avp.getInteger64()); - else if (avpRep.getType().equals("Unsigned32")) - value = String.valueOf(avp.getUnsigned32()); - else if (avpRep.getType().equals("Float32")) - value = String.valueOf(avp.getFloat32()); - else - //value = avp.getOctetString(); - value = new String(avp.getOctetString(), StandardCharsets.UTF_8); - - log.info(prefix + ""); - } - } - } - - - - - /** - * @return - */ - private boolean finished() { - return this.finished; - } - - public static void main(String[] args) { - ExampleServer es = new ExampleServer(); - es.initStack(); - - while (!es.finished()) { - try { - Thread.currentThread().sleep(5000); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - /* - * (non-Javadoc) - * - * @see - * org.jdiameter.api.NetworkReqListener#processRequest(org.jdiameter.api - * .Request) - */ - @Override - public Answer processRequest(Request request) { - dumpMessage(request,false); - if (request.getCommandCode() != commandCode) { - log.error("Received bad answer: " + request.getCommandCode()); - return null; - } - AvpSet requestAvpSet = request.getAvps(); - - Avp exchangeTypeAvp = requestAvpSet.getAvp(exchangeTypeCode, vendorID); - Avp exchangeDataAvp = requestAvpSet.getAvp(exchangeDataCode, vendorID); - if (exchangeTypeAvp == null) { - log.error("Request does not have Exchange-Type"); - - Answer answer = createAnswer(request, 5004, EXCHANGE_TYPE_TERMINATING); - dumpMessage(answer,true); - return answer; // set - // exchange - // type - // to - // terminating - } - if (exchangeDataAvp == null) { - log.error("Request does not have Exchange-Data"); - Answer answer = createAnswer(request, 5004, EXCHANGE_TYPE_TERMINATING); - dumpMessage(answer,true); - return answer; // set - // exchange - // type - // to - // terminating - } - // cast back to int(Enumerated is Unsigned32, and API represents it as - // long so its easier - // to manipulate - try { - switch ((int) exchangeTypeAvp.getUnsigned32()) { - case EXCHANGE_TYPE_INITIAL: - // JIC check; - String data = exchangeDataAvp.getUTF8String(); - this.session = this.factory.getNewSession(request.getSessionId()); - if (data.equals(TO_RECEIVE[toReceiveIndex])) { - // create session; - - Answer answer = createAnswer(request, 2001, EXCHANGE_TYPE_INITIAL); // set - // exchange - // type - // to - // terminating - toReceiveIndex++; - dumpMessage(answer,true); - return answer; - } else { - log.error("Received wrong Exchange-Data: " + data); - Answer answer = request.createAnswer(6000); - } - break; - case EXCHANGE_TYPE_INTERMEDIATE: - // JIC check; - data = exchangeDataAvp.getUTF8String(); - if (data.equals(TO_RECEIVE[toReceiveIndex])) { - - Answer answer = createAnswer(request, 2001, EXCHANGE_TYPE_INTERMEDIATE); // set - // exchange - // type - // to - // terminating - toReceiveIndex++; - dumpMessage(answer,true); - return answer; - } else { - log.error("Received wrong Exchange-Data: " + data); - } - break; - case EXCHANGE_TYPE_TERMINATING: - data = exchangeDataAvp.getUTF8String(); - if (data.equals(TO_RECEIVE[toReceiveIndex])) { - // good, we reached end of FSM. - finished = true; - // release session and its resources. - Answer answer = createAnswer(request, 2001, EXCHANGE_TYPE_TERMINATING); // set - // exchange - // type - // to - // terminating - toReceiveIndex++; - this.session.release(); - finished = true; - this.session = null; - dumpMessage(answer,true); - return answer; - - } else { - log.error("Received wrong Exchange-Data: " + data); - } - break; - default: - log.error("Bad value of Exchange-Type avp: " + exchangeTypeAvp.getUnsigned32()); - break; - } - } catch (AvpDataException e) { - // thrown when interpretation of byte[] fails - e.printStackTrace(); - } catch (InternalException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - //error, something bad happened. - finished = true; - return null; - } - - private Answer createAnswer(Request r, int resultCode, int enumType) { - Answer answer = r.createAnswer(resultCode); - AvpSet answerAvps = answer.getAvps(); - // code , value , vendor, mandatory,protected,isUnsigned32 - // (Enumerated) - Avp exchangeType = answerAvps.addAvp(exchangeTypeCode, (long) enumType, vendorID, true, false, true); // value - // is - // set - // on - // creation - // code , value , vendor, mandatory,protected, isOctetString - Avp exchengeData = answerAvps.addAvp(exchangeDataCode, TO_RECEIVE[toReceiveIndex], vendorID, true, false, false); // value - // is - // set - // on - // creation - - - //add origin, its required by duplicate detection - answerAvps.addAvp(Avp.ORIGIN_HOST, stack.getMetaData().getLocalPeer().getUri().getFQDN(), true, false, true); - answerAvps.addAvp(Avp.ORIGIN_REALM, stack.getMetaData().getLocalPeer().getRealmName(), true, false, true); - return answer; - } +public class ExampleServer implements NetworkReqListener +{ + private static final Logger log = Logger.getLogger(ExampleServer.class); + + static { + + configLog4j(); + + } + + private static void configLog4j() + { + InputStream inStreamLog4j = ExampleServer.class.getClassLoader().getResourceAsStream("log4j.properties"); + Properties propertiesLog4j = new Properties(); + try { + propertiesLog4j.load(inStreamLog4j); + PropertyConfigurator.configure(propertiesLog4j); + } + catch (Exception e) { + e.printStackTrace(); + } + + log.debug("log4j configured"); + + } + + private static final String configFile = "org/example/server/server-jdiameter-config.xml"; + private static final String dictionaryFile = "org/example/client/dictionary.xml"; + private static final String realmName = "exchange.example.org"; + // Defs for our app + private static final int commandCode = 686; + private static final long vendorID = 66666; + private static final long applicationID = 33333; + private ApplicationId authAppId = ApplicationId.createByAuthAppId(applicationID); + ; + private static final int exchangeTypeCode = 888; + private static final int exchangeDataCode = 999; + // enum values for Exchange-Type AVP + private static final int EXCHANGE_TYPE_INITIAL = 0; + private static final int EXCHANGE_TYPE_INTERMEDIATE = 1; + private static final int EXCHANGE_TYPE_TERMINATING = 2; + + private static final String[] TO_RECEIVE = new String[]{"I want to get 3 answers", "This is second message", "Bye bye"}; + private AvpDictionary dictionary = AvpDictionary.INSTANCE; + private Stack stack; + private SessionFactory factory; + + // //////////////////////////////////////// + // Objects which will be used in action // + // //////////////////////////////////////// + private Session session; + private int toReceiveIndex = 0; + private boolean finished = false; + + private void initStack() + { + if (log.isInfoEnabled()) { + log.info("Initializing Stack..."); + } + InputStream is = null; + try { + dictionary.parseDictionary(this.getClass().getClassLoader().getResourceAsStream(dictionaryFile)); + log.info("AVP Dictionary successfully parsed."); + this.stack = new StackImpl(); + + is = this.getClass().getClassLoader().getResourceAsStream(configFile); + + Configuration config = new XMLConfiguration(is); + factory = stack.init(config); + if (log.isInfoEnabled()) { + log.info("Stack Configuration successfully loaded."); + } + + Set appIds = stack.getMetaData().getLocalPeer().getCommonApplications(); + + log.info("Diameter Stack :: Supporting " + appIds.size() + " applications."); + for (org.jdiameter.api.ApplicationId x : appIds) { + log.info("Diameter Stack :: Common :: " + x); + } + is.close(); + Network network = stack.unwrap(Network.class); + network.addNetworkReqListener(this, this.authAppId); + } + catch (Exception e) { + e.printStackTrace(); + if (this.stack != null) { + this.stack.destroy(); + } + + if (is != null) { + try { + is.close(); + } + catch (IOException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + return; + } + + MetaData metaData = stack.getMetaData(); + if (metaData.getStackType() != StackType.TYPE_SERVER || metaData.getMinorVersion() <= 0) { + stack.destroy(); + if (log.isEnabledFor(org.apache.log4j.Level.ERROR)) { + log.error("Incorrect driver"); + } + return; + } + + try { + if (log.isInfoEnabled()) { + log.info("Starting stack"); + } + stack.start(); + if (log.isInfoEnabled()) { + log.info("Stack is running."); + } + } + catch (Exception e) { + e.printStackTrace(); + stack.destroy(); + return; + } + if (log.isInfoEnabled()) { + log.info("Stack initialization successfully completed."); + } + } + + private void dumpMessage(Message message, boolean sending) + { + if (log.isInfoEnabled()) { + log.info((sending ? "Sending " : "Received ") + (message.isRequest() ? "Request: " : "Answer: ") + message.getCommandCode() + "\nE2E:" + + message.getEndToEndIdentifier() + "\nHBH:" + message.getHopByHopIdentifier() + "\nAppID:" + message.getApplicationId()); + log.info("AVPS[" + message.getAvps().size() + "]: \n"); + try { + printAvps(message.getAvps()); + } + catch (AvpDataException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + private void printAvps(AvpSet avpSet) throws AvpDataException + { + printAvpsAux(avpSet, 0); + } + + /** + * Prints the AVPs present in an AvpSet with a specified 'tab' level + * + * @param avpSet the AvpSet containing the AVPs to be printed + * @param level an int representing the number of 'tabs' to make a pretty + * print + * @throws AvpDataException + */ + private void printAvpsAux(AvpSet avpSet, int level) throws AvpDataException + { + String prefix = " ".substring(0, level * 2); + + for (Avp avp : avpSet) { + AvpRepresentation avpRep = AvpDictionary.INSTANCE.getAvp(avp.getCode(), avp.getVendorId()); + + if (avpRep != null && avpRep.getType().equals("Grouped")) { + log.info(prefix + ""); + printAvpsAux(avp.getGrouped(), level + 1); + log.info(prefix + ""); + } + else if (avpRep != null) { + String value = ""; + + if (avpRep.getType().equals("Integer32")) + value = String.valueOf(avp.getInteger32()); + else if (avpRep.getType().equals("Integer64") || avpRep.getType().equals("Unsigned64")) + value = String.valueOf(avp.getInteger64()); + else if (avpRep.getType().equals("Unsigned32")) + value = String.valueOf(avp.getUnsigned32()); + else if (avpRep.getType().equals("Float32")) + value = String.valueOf(avp.getFloat32()); + else + //value = avp.getOctetString(); + value = new String(avp.getOctetString(), StandardCharsets.UTF_8); + + log.info(prefix + ""); + } + } + } + + + /** + * @return + */ + private boolean finished() + { + return this.finished; + } + + public static void main(String[] args) + { + ExampleServer es = new ExampleServer(); + es.initStack(); + + while (!es.finished()) { + try { + Thread.currentThread().sleep(5000); + } + catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + /* + * (non-Javadoc) + * + * @see + * org.jdiameter.api.NetworkReqListener#processRequest(org.jdiameter.api + * .Request) + */ + @Override + public Answer processRequest(Request request) + { + dumpMessage(request, false); + if (request.getCommandCode() != commandCode) { + log.error("Received bad answer: " + request.getCommandCode()); + return null; + } + AvpSet requestAvpSet = request.getAvps(); + + Avp exchangeTypeAvp = requestAvpSet.getAvp(exchangeTypeCode, vendorID); + Avp exchangeDataAvp = requestAvpSet.getAvp(exchangeDataCode, vendorID); + if (exchangeTypeAvp == null) { + log.error("Request does not have Exchange-Type"); + + Answer answer = createAnswer(request, 5004, EXCHANGE_TYPE_TERMINATING); + dumpMessage(answer, true); + return answer; // set + // exchange + // type + // to + // terminating + } + if (exchangeDataAvp == null) { + log.error("Request does not have Exchange-Data"); + Answer answer = createAnswer(request, 5004, EXCHANGE_TYPE_TERMINATING); + dumpMessage(answer, true); + return answer; // set + // exchange + // type + // to + // terminating + } + // cast back to int(Enumerated is Unsigned32, and API represents it as + // long so its easier + // to manipulate + try { + switch ((int) exchangeTypeAvp.getUnsigned32()) { + case EXCHANGE_TYPE_INITIAL: + // JIC check; + String data = exchangeDataAvp.getUTF8String(); + this.session = this.factory.getNewSession(request.getSessionId()); + if (data.equals(TO_RECEIVE[toReceiveIndex])) { + // create session; + + Answer answer = createAnswer(request, 2001, EXCHANGE_TYPE_INITIAL); // set + // exchange + // type + // to + // terminating + toReceiveIndex++; + dumpMessage(answer, true); + return answer; + } + else { + log.error("Received wrong Exchange-Data: " + data); + Answer answer = request.createAnswer(6000); + } + break; + case EXCHANGE_TYPE_INTERMEDIATE: + // JIC check; + data = exchangeDataAvp.getUTF8String(); + if (data.equals(TO_RECEIVE[toReceiveIndex])) { + + Answer answer = createAnswer(request, 2001, EXCHANGE_TYPE_INTERMEDIATE); // set + // exchange + // type + // to + // terminating + toReceiveIndex++; + dumpMessage(answer, true); + return answer; + } + else { + log.error("Received wrong Exchange-Data: " + data); + } + break; + case EXCHANGE_TYPE_TERMINATING: + data = exchangeDataAvp.getUTF8String(); + if (data.equals(TO_RECEIVE[toReceiveIndex])) { + // good, we reached end of FSM. + finished = true; + // release session and its resources. + Answer answer = createAnswer(request, 2001, EXCHANGE_TYPE_TERMINATING); // set + // exchange + // type + // to + // terminating + toReceiveIndex++; + this.session.release(); + finished = true; + this.session = null; + dumpMessage(answer, true); + return answer; + + } + else { + log.error("Received wrong Exchange-Data: " + data); + } + break; + default: + log.error("Bad value of Exchange-Type avp: " + exchangeTypeAvp.getUnsigned32()); + break; + } + } + catch (AvpDataException e) { + // thrown when interpretation of byte[] fails + e.printStackTrace(); + } + catch (InternalException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //error, something bad happened. + finished = true; + return null; + } + + private Answer createAnswer(Request r, int resultCode, int enumType) + { + Answer answer = r.createAnswer(resultCode); + AvpSet answerAvps = answer.getAvps(); + // code , value , vendor, mandatory,protected,isUnsigned32 + // (Enumerated) + Avp exchangeType = answerAvps.addAvp(exchangeTypeCode, (long) enumType, vendorID, true, false, true); // value + // is + // set + // on + // creation + // code , value , vendor, mandatory,protected, isOctetString + Avp exchengeData = answerAvps.addAvp(exchangeDataCode, TO_RECEIVE[toReceiveIndex], vendorID, true, false, false); // value + // is + // set + // on + // creation + + + //add origin, its required by duplicate detection + answerAvps.addAvp(Avp.ORIGIN_HOST, stack.getMetaData().getLocalPeer().getUri().getFQDN(), true, false, true); + answerAvps.addAvp(Avp.ORIGIN_REALM, stack.getMetaData().getLocalPeer().getRealmName(), true, false, true); + return answer; + } } diff --git a/lombok.config b/lombok.config new file mode 100644 index 000000000..2066d75ae --- /dev/null +++ b/lombok.config @@ -0,0 +1 @@ +lombok.log.fieldName=LOG diff --git a/pom.xml b/pom.xml index 0840de2d9..eae064e94 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ core + diameter-quarkus @@ -31,6 +32,7 @@ 3.2.5 3.1.2 3.0.0 + 1.18.32 3.11.0 3.1.2 @@ -53,6 +55,11 @@ ${junit.version} test + + org.projectlombok + lombok + ${lombok.version} + @@ -66,6 +73,36 @@ true + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + install + + sign + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + verify + + jar + + + +