Quarkus: Smallrye Kafka configure channels for distinct bootstrap servers using diferent KEYS and PASSWORD - apache-kafka

My goal is to produce events in 2 different channels using distinct bootstrap servers using jaas configuration using SASL_SSL, but I am not able to setup the channels to authenticate correctly on the bootstrap servers.
I've tried the following setup
mp.messaging.outgoing.channel1.bootstrap.servers=${KAFKA1}
mp.messaging.outgoing.channel1.ssl.endpoint-identification-algorithm=https
mp.messaging.outgoing.channel1.security.protocol=SASL_SSL
mp.messaging.outgoing.channel1.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="${KEY1}" password="${PWD1}";
mp.messaging.outgoing.channel1.sasl.mechanism=PLAIN
mp.messaging.outgoing.channel2.bootstrap.servers=${KAFKA2}
mp.messaging.outgoing.channel2.ssl.endpoint-identification-algorithm=https
mp.messaging.outgoing.channel2.security.protocol=SASL_SSL
mp.messaging.outgoing.channel2.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="${KEY2}" password="${PWD2}";
mp.messaging.outgoing.channel2.sasl.mechanism=PLAIN
Using this setup I am receiving errors on the channel initialization.
2023-01-18 13:57:10 13:57:10.445 ERROR [Application] (main) Failed to start application (with profile prod): java.lang.IllegalArgumentException: Could not find a 'KafkaClient' entry in the JAAS configuration. System property 'java.security.auth.login.config' is not set
2023-01-18 13:57:10 at org.apache.kafka.common.security.JaasContext.defaultContext(JaasContext.java:131)
2023-01-18 13:57:10 at org.apache.kafka.common.security.JaasContext.load(JaasContext.java:96)
2023-01-18 13:57:10 at org.apache.kafka.common.security.JaasContext.loadClientContext(JaasContext.java:82)
2023-01-18 13:57:10 at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:167)
2023-01-18 13:57:10 at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:81)
2023-01-18 13:57:10 at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:105)
The initial setup used the default bootstrap settings and it worked fined until KAFKA was brought to the equation.
kafka.bootstrap.servers='${KAFKA1}'
kafka.ssl.endpoint-identification-algorithm=https
kafka.security.protocol=SASL_SSL
kafka.sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required username="${Key1}" password="${PWD1}";
kafka.sasl.mechanism=PLAIN
I've tried the described on the issue and I am not being able figure out how to configure the channels to authenticate to 2 different bootstrap servers.

As the error says, you need a JAAS conf set in your JVM system properties
-Djava.security.auth.login.config=/path/to/kafka-jaas.conf

After reading the documentation [https://quarkus.io/guides/kafka] the option
1
kafka-configuration Allows the config1
Therefore the solution is to implement a provider bean
#ApplicationScoped
#Slf4j
public class KafkaConfigBean {
#Produces
#Identifier("kafka1")
public Map<String, Object> kafkaConfig() {
HashMap<String, Object> config = new HashMap<>();
config.put("security.protocol", "SASL_SSL");
config.put("sasl.mechanism", "PLAIN");
String saslConfig = String.format("org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%S\" password=\"%s\";",
System.getenv("KEY1"), System.getenv("PWD1"));
config.put("sasl.jaas.config", saslConfig);
log.info("Initialized Kafka 1 config");
return config;
}
#Produces
#Identifier("kafka2")
public Map<String, Object> kafkaConfigPTT() {
HashMap<String, Object> config = new HashMap<>();
config.put("security.protocol", "SASL_SSL");
config.put("sasl.mechanism", "PLAIN");
String saslConfig = String.format("org.apache.kafka.common.security.plain.PlainLoginModule required username=\"%S\" password=\"%s\";",
System.getenv("KEY2"), System.getenv("PWD2"));
config.put("sasl.jaas.config", saslConfig);
log.info("Initialized Kafka 2 config");
return config;
}
}
Thus resulting on the following configuration file
mp.messaging.outgoing.channel1.bootstrap.servers=\${KAFKA1}
mp.messaging.outgoing.channel1.kafka-configuration=kafka1
mp.messaging.outgoing.channel2.bootstrap.servers=\${KAFKA2}
mp.messaging.outgoing.channel2.kafka-configuration=kafka2

Related

Error when trying to access Kafka with Quarkus in native mode

I tried a simple sample code to test access to a "kerberized" Kafka from Quarkus 2.2.2 with smallrye-reactive-messaging-kafka :
package org.acme;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.eclipse.microprofile.reactive.messaging.Incoming;
import javax.enterprise.context.ApplicationScoped;
#ApplicationScoped
public class MyTopicConsumer {
#Incoming("in")
public void consume(ConsumerRecord<String, String> record) {
System.out.println("read from Kafka : " + record.value() ) ;
}
}
Kafkas is behind Kerberos, so i used an application.properties like this :
quarkus.ssl.native=true
quarkus.native.enable-all-security-services=true
mp.messaging.incoming.in.group.id=my-group
mp.messaging.incoming.in.auto.commit.interval.ms=1000
mp.messaging.incoming.in.security.protocol=SASL_SSL
mp.messaging.incoming.in.sasl.kerberos.service.name=kafka
mp.messaging.incoming.in.sasl.mechanism=GSSAPI
mp.messaging.incoming.in.sasl.jaas.config=com.sun.security.auth.module.Krb5LoginModule "required" doNotPrompt=true useKeyTab=true storeKey=true serviceName="kafka" keyTab="<keytab>" principal="<principal>" useTicketCache=false;
mp.messaging.incoming.in.ssl.truststore.location=<location>
mp.messaging.incoming.in.ssl.truststore.password=<password>
mp.messaging.incoming.in.connector=smallrye-kafka
mp.messaging.incoming.in.topic=<topic>
mp.messaging.incoming.in.auto.offset.reset=earliest
mp.messaging.incoming.in.enable.auto.commit=false
mp.messaging.incoming.in.bootstrap.servers=<list of servers>
It works nicely in jvm mode, but fails in native mode (graalvm-ce-java11-21.2.0) with this error :
ERROR [io.sma.rea.mes.provider] (main) SRMSG00230: Unable to create the publisher or subscriber during initialization: org.apache.kafka.common.KafkaException: Failed to construct kafka consumer
at org.apache.kafka.clients.consumer.KafkaConsumer.<init>(KafkaConsumer.java:823)
at org.apache.kafka.clients.consumer.KafkaConsumer.<init>(KafkaConsumer.java:665)
at io.smallrye.reactive.messaging.kafka.impl.ReactiveKafkaConsumer.<init>(ReactiveKafkaConsumer.java:80)
at io.smallrye.reactive.messaging.kafka.impl.KafkaSource.<init>(KafkaSource.java:85)
at io.smallrye.reactive.messaging.kafka.KafkaConnector.getPublisherBuilder(KafkaConnector.java:182)
at io.smallrye.reactive.messaging.kafka.KafkaConnector_ClientProxy.getPublisherBuilder(KafkaConnector_ClientProxy.zig:159)
at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.createPublisherBuilder(ConfiguredChannelFactory.java:190)
at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.register(ConfiguredChannelFactory.java:153)
at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory.initialize(ConfiguredChannelFactory.java:125)
at io.smallrye.reactive.messaging.impl.ConfiguredChannelFactory_ClientProxy.initialize(ConfiguredChannelFactory_ClientProxy.zig:189)
at java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
at io.smallrye.reactive.messaging.extension.MediatorManager.start(MediatorManager.java:189)
at io.smallrye.reactive.messaging.extension.MediatorManager_ClientProxy.start(MediatorManager_ClientProxy.zig:220)
at io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingLifecycle.onApplicationStart(SmallRyeReactiveMessagingLifecycle.java:41)
at io.quarkus.smallrye.reactivemessaging.runtime.SmallRyeReactiveMessagingLifecycle_Observer_onApplicationStart_4e8937813d9e8faff65c3c07f88fa96615b70e70.notify(SmallRyeReactiveMessagingLifecycle_Observer_onApplicationStart_4e8937813d9e8faff65c3c07f88fa96615b70e70.zig:111)
at io.quarkus.arc.impl.EventImpl$Notifier.notifyObservers(EventImpl.java:300)
at io.quarkus.arc.impl.EventImpl$Notifier.notify(EventImpl.java:282)
at io.quarkus.arc.impl.EventImpl.fire(EventImpl.java:70)
at io.quarkus.arc.runtime.ArcRecorder.fireLifecycleEvent(ArcRecorder.java:128)
at io.quarkus.arc.runtime.ArcRecorder.handleLifecycleEvents(ArcRecorder.java:97)
at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy_0(LifecycleEventsBuildStep$startupEvent1144526294.zig:87)
at io.quarkus.deployment.steps.LifecycleEventsBuildStep$startupEvent1144526294.deploy(LifecycleEventsBuildStep$startupEvent1144526294.zig:40)
at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:623)
at io.quarkus.runtime.Application.start(Application.java:101)
at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:101)
at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
Caused by: org.apache.kafka.common.KafkaException: org.apache.kafka.common.KafkaException: Could not find a public no-argument constructor for org.apache.kafka.common.security.kerberos.KerberosLogin
at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:184)
at org.apache.kafka.common.network.ChannelBuilders.create(ChannelBuilders.java:192)
at org.apache.kafka.common.network.ChannelBuilders.clientChannelBuilder(ChannelBuilders.java:81)
at org.apache.kafka.clients.ClientUtils.createChannelBuilder(ClientUtils.java:105)
at org.apache.kafka.clients.consumer.KafkaConsumer.<init>(KafkaConsumer.java:737)
... 30 more
I tried a few changes suggested by some posts, but with no effect.
Can anyone suggest how to fix or workaround this ?
Thanks
It is seems that the code doesn't contain the constructor org.apache.kafka.common.security.kerberos.KerberosLogin.
Have you try to add the class as describe in https://quarkus.io/guides/writing-native-applications-tips#registering-for-reflection
Maybe you need to add line in your configuration file
quarkus.native.additional-build-args=-H:ReflectionConfigurationFiles=reflection-config.json
And add the class org.apache.kafka.common.security.kerberos.KerberosLogin in reflection-config.json as describe here https://quarkus.io/guides/writing-native-applications-tips#using-a-configuration-file

Kafka - Retries and Recovery not invoked

I have implemented a custom listener i.e. not using the #KafkaListener annotation due to the fact that my application need to dynamically lsiten to topics. I have seen suggestions of moving to Spring kafka 2.6.x but I can't upgrade due to the fact that I am stuck (at least for now) with Spring 5.1.X.RELEASE which means I can only use Spring-kafka 2.2.x.
My question is, how can I achieve retry, recovery and error handling with Spring-kafka 2.2.x?
ConcurrentKafkaListenerContainerFactory listenerFactory = new ConcurrentKafkaListenerContainerFactory();
listenerFactory.setConsumerFactory(consumerFactory);
configurer.configure(listenerFactory, consumerFactory);
listenerFactory.setConcurrency(listenerConcurrency);
listenerFactory.setStatefulRetry(Boolean.TRUE);
listenerFactory.setBatchListener(isBatchListener);
listenerFactory.getContainerProperties().setTransactionManager(chainedKafkaTransactionManager);
listenerFactory.getContainerProperties().setAckOnError(false);
listenerFactory.setRetryTemplate(retryTemplate(kafkaEhCacheRetryManager));
listenerFactory.setRecoveryCallback(kafkaRecoverer);
My retry template looks like:
RetryTemplate retryTemplate(EhCacheCacheManager kafkaEhCacheRetryManager) {
ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
exponentialBackOffPolicy.setInitialInterval(initialIntervalForRetries);
exponentialBackOffPolicy.setSleeper(new ThreadWaitSleeper());
exponentialBackOffPolicy.setMultiplier(2.0);
exponentialBackOffPolicy.setMaxInterval(maxIntervalForRetries);
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setRetryContextCache(new KafkaEhRetryContextCache(kafkaEhCacheRetryManager));
retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);
// KafkaTransactionalRetryPolicy extends SimpleRetryPolicy
KafkaTransactionalRetryPolicy retryPolicy = new KafkaTransactionalRetryPolicy(kafkaTemplate);
retryPolicy.setMaxAttempts(maxAttempts);
retryTemplate.setRetryPolicy(kafkaTransactionalRetryPolicy);
return retryTemplate;
}
My listener looks like:
public class MyKafkaListener implements MessageListener<String, String> {
#Override
#Transactional(value = "chainedKafkaTransactionManager")
public void onMessage(final ConsumerRecord<String, String> consumerRecord){
throw new RuntimeException("thrown out of out anger");
}
}
with this config:
spring.kafka:
bootstrap-servers: ${service.kakfa.host}
admin:
client-id: test-consumers
bootstrap-servers: ${service.kakfa.host}
consumer:
bootstrap-servers: ${service.kakfa.host}
group-id: local-consumers
client-id: local-consumers
auto-offset-reset: earliest
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
enable-auto-commit: false
isolation-level: read_committed
producer:
bootstrap-servers: ${service.kakfa.host}
client-id: local-producer
acks: all
retries: 3
transaction-id-prefix: local-producer-tx-
properties:
enable.idempotence: true
transactional.id: tran-id-1-
max.in.flight.requests.per.connection: 5
listener.concurrency: 1
I have seen several examples on StackOverflow on how to do this, but none has worked so far.
The retry mechanism you are trying to use only applies to #KafakListeners. It is built into a listener adapter used to call the listener POJO.
In newer versions, the SeekToCurrentErrorHandler and DefaultAfterRollbackProcessor have a back off (Since 2.3), eliminating the need for a retry template at the listener level, in favor of retry at the container level.
With your own listener you would have to use a RetryTemplate within the listener code itself.
BTW, Spring 5.1.x is no longer supported https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions#supported-versions

How to connect to Kafka through JMS on OpenLiberty?

Im trying to connect to Kafka with JMS. I followed this guide to use the Payara Kafka Connector. This worked on Wildfly. But I cant get it to work on OpenLiberty.
The server.xml:
<resourceAdapter id="kafkajmsra" location="${shared.resource.dir}kafka-rar-0.5.0.rar"/>
<jmsTopicConnectionFactory jndiName="JMSTopicFactory">
<properties.kafkajmsra
bootstrapServerConfig="kafka:9092"/>
</jmsTopicConnectionFactory>
<jmsTopic id="kafkaTopic" jndiName="JmsTopic">
<properties.kafkajmsra topicName="demoTopic" />
</jmsTopic>
With those configurations I get a NullPointerException if I try to inject those components. The JNDI names can be found but not with these parameters.
#Resource(lookup = "JMSTopicFactory")
private TopicConnectionFactory jmsTopicFactory;
#Resource(lookup = "JMSTopic")
private Topic jmsTopic;
Am I missing something in the server.xml?
I tried using the default JMS Connector. It does connect to Kafka, but the connection gets refused and on the kafka side it tells me this:
[2020-05-31 20:05:27,134] WARN [SocketServer brokerId=1] Unexpected error from /172.20.0.4; closing connection (org.apache.kafka.common.network.Selector)
org.apache.kafka.common.network.InvalidReceiveException: Invalid receive (size = -1091633152)
at org.apache.kafka.common.network.NetworkReceive.readFrom(NetworkReceive.java:103)
at org.apache.kafka.common.network.KafkaChannel.receive(KafkaChannel.java:448)
at org.apache.kafka.common.network.KafkaChannel.read(KafkaChannel.java:398)
at org.apache.kafka.common.network.Selector.attemptRead(Selector.java:678)
at org.apache.kafka.common.network.Selector.pollSelectionKeys(Selector.java:580)
at org.apache.kafka.common.network.Selector.poll(Selector.java:485)
at kafka.network.Processor.poll(SocketServer.scala:893)
at kafka.network.Processor.run(SocketServer.scala:792)
at java.lang.Thread.run(Thread.java:748)
EDIT:
I changed the server.xml to look like this now:
<resourceAdapter id="kafkajmsra" location="${shared.resource.dir}/kafka-rar-0.4.0.rar"/>
<connectionFactory jndi="java:app/KafkaConnectionFactory"
interfaceName="fish.payara.cloud.connectors.kafka.api.KafkaConnectionFactory"
resourceAdapter="liberty/wlp/usr/shared/resources/kafka-rar-0.4.0.rar">
</connectionFactory>
and the java code looks like this:
#ApplicationScoped
public class TopicProducer {
private static final Logger LOG = LoggerFactory.getLogger(TopicProducer.class);
public TopicProducer() throws Exception {
LOG.info("Starting TopicProducer");
}
#Resource(lookup = "java:app/KafkaConnectionFactory")
KafkaConnectionFactory kafkaConnectionFactory;
public void send(final String msg) {
try (KafkaConnection connection = kafkaConnectionFactory.createConnection()) {
LOG.info("Send message: {}", msg);
connection.send(new ProducerRecord("demoTopic", msg));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
But now I get a NullPointerException on the #Resource. My guess is that the resource adapter cannot be found.

How to connect JBoss 7.1.1 remoting -jmx via java code?

I have a JBoss 7.1.1 server, for which I want to write jmx client. As far I understood, jboss 7.1.1 is not using typical rmi based jmx and they have given a layer of remoting-jmx over native management. I am using following code:
JMXServiceURL address = new JMXServiceURL("service:jmx:remoting-jmx://localhost:9999");
Map env = JMXConnectorConfig.getEnvironment(paramtbl);
JMXConnector connector = JMXConnectorFactory.connect(address, env);
But it is giving following exception:
java.net.MalformedURLException: Unsupported protocol: remoting-jmx
I googled it and the following thread seems relevant:
https://community.jboss.org/thread/204653?tstart=0
It asks to add jboss's libraries to my classpath. I tried that also but still getting same exception.
I got the same exception when trying to get a JmxServiceUrl.
Make sure that in your standalone.xml you have the following:
<subsystem xmlns="urn:jboss:domain:jmx:1.1">
<show-model value="true"/>
<remoting-connector use-management-endpoint="true" />
</subsystem>
And you should include in project classpath the jar named: jboss-client.jar, it can be found in JBOSS_DIRECTORY/bin/client. In fact, the JMX client must include that jar in its classpath.
This tip fixed the problem for me..Hope it will be helpful for you
Tried to do the same from Arquillian test on JBoss AS7 and finally had to use:
import org.jboss.remotingjmx.RemotingConnectorProvider;
RemotingConnectorProvider s = new RemotingConnectorProvider();
JMXConnector connector = s.newJMXConnector(url, credentials);
connector.connect();
Could not have "module name="org.jboss.remoting-jmx" services="import"" working
Also works with
environment.put("jmx.remote.protocol.provider.pkgs", "org.jboss.remotingjmx");
JMXConnector connector = JMXConnectorFactory.connect(url, environment);
connector.connect();
I used this code to connect to JBoss in a remote server
ModelControllerClient client = null;
try {
client = createClient(InetAddress.getByName("172.16.73.12"), 9999,
"admin", "pass", "ManagementRealm");
}
catch (UnknownHostException e) {
e.printStackTrace();
}
Where createClient is a method I wrote -
private ModelControllerClient createClient(final InetAddress host,
final int port, final String username, final String password,
final String securityRealmName) {
final CallbackHandler callbackHandler = new CallbackHandler() {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (Callback current : callbacks) {
if (current instanceof NameCallback) {
NameCallback ncb = (NameCallback) current;
ncb.setName(username);
} else if (current instanceof PasswordCallback) {
PasswordCallback pcb = (PasswordCallback) current;
pcb.setPassword(password.toCharArray());
} else if (current instanceof RealmCallback) {
RealmCallback rcb = (RealmCallback) current;
rcb.setText(rcb.getDefaultText());
} else {
throw new UnsupportedCallbackException(current);
}
}
}
};
return ModelControllerClient.Factory
.create(host, port, callbackHandler);
}
For more information on how to read the data obtained from Server or for the complete project using Java/Google visualizer API (to show the statistics in Graph after every 10 secs) , Please refer to this tutorial -
http://javacodingtutorial.blogspot.com/2014/05/reading-jboss-memory-usage-using-java.html
Add the following to your jboss-deployment-structure
<dependencies>
<module name="org.jboss.remoting3.remoting-jmx" services="import"/>
</dependencies>
Activate JMX remoting subsystem by adding following entry in standalone.xml
<subsystem xmlns="urn:jboss:domain:ee:1.1">
<!-- Activate JMX remoting -->
<global-modules>
<module name="org.jboss.remoting-jmx" slot="main"/>
</global-modules>
...
</subsystem>
It seems like "jboss-client.jar" is not available at run-time for JMX connection, So make sure that you have added "jboss-client.jar" in the class path.
And also you are using deprecated protocol "remoting-jmx" instead of "remote".
i.e, "service:jmx:remote://localhost:9999"
Hope it helps.

JBoss EJB3 MDB config

I am trying to understand some EJB 3 code running in JBoss 4.3.
We've got an ejb3-interceptors-aop.xml file configured in JBoss with some MDB configuration and then we've got the MDB Java class.
What I'd like to understand is when and how does the MDB get "bound" to the MQ? That is, when/how does the MDB start listening to the MQ queue?
Does JBoss at startup read the ejb3-interceptors-aop.xml file and then find the class with the AspectDomain annotation equal to "GatewayMDB" and "bind" to the MQ queue at startup?
XML in ejb3-interceptors-aop.xml:
<domain name="GatewayMDB">
<bind pointcut="execution(public * #javax.annotation.security.RunAs->*(..))">
<interceptor-ref name="org.jboss.ejb3.security.RunAsSecurityInterceptorFactory"/>
</bind>
<bind pointcut="execution(public * *->*(..))">
<interceptor-ref name="org.jboss.ejb3.stateless.StatelessInstanceInterceptor"/>
<interceptor-ref name="org.jboss.ejb3.tx.TxInterceptorFactory"/>
<interceptor-ref name="org.jboss.ejb3.AllowedOperationsInterceptor"/>
<interceptor-ref name="org.jboss.ejb3.entity.TransactionScopedEntityManagerInterceptor"/>
<interceptor-ref name="org.jboss.ejb3.interceptor.EJB3InterceptorsFactory"/>
</bind>
<annotation expr="!class(#org.jboss.annotation.ejb.PoolClass)">
#org.jboss.annotation.ejb.PoolClass (value=org.jboss.ejb3.StrictMaxPool.class, maxSize=30, timeout=10000)
</annotation>
<annotation expr="!class(#org.jboss.annotation.ejb.DefaultActivationSpecs)">
#org.jboss.annotation.ejb.DefaultActivationSpecs ({#javax.ejb.ActivationConfigProperty(propertyName = "channel", propertyValue = "SYSTEM.DEF.SVRCONN"), #javax.ejb.ActivationConfigProperty(propertyName = "hostName", propertyValue = "10.10.10.10"), #javax.ejb.ActivationConfigProperty(propertyName = "queueManager", propertyValue = "QM"), #javax.ejb.ActivationConfigProperty(propertyName = "port", propertyValue = "1419"),#javax.ejb.ActivationConfigProperty(propertyName = "transportType", propertyValue = "CLIENT")})
</annotation>
</domain>
MDB class:
#MessageDriven(name = "BridgeMDB", activationConfig = {
#ActivationConfigProperty(propertyName = "useJNDI", propertyValue = "true"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "TO.WLS.LQUEUE.BG"),
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "maxPoolDepth", propertyValue = "1") })
#ResourceAdapter("wmq.jmsra.rar")
#AspectDomain("GatewayMDB")
#Interceptors(SpringBeanAutowiringInterceptor.class)
#TransactionManagement(TransactionManagementType.CONTAINER)
public class BridgeMDB implements MessageListener {
private static Logger logger = Logger.getLogger(BridgeMDB.class);
#Autowired
private MessageProcessor messageProcessor;
#Autowired
private MessageTranslator messageTranslator;
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public void onMessage(Message message) {
...
}
}
Disclaimer: this is a supposition, since I don't know the jboss code.
The common way of processing class files in java is to read them through the class path (in this case it would be at load-time) and construct some sort of metadata for each class.
Then, when the application bootstraps the container will read the class's metadatada to wire/inject/configure the appropriate attributes that were defined within the class.
As for the xml, most of the jboss configuration is static AFAIK, ie, you have to restart the app server in order for changes to take effect.
So all in all, I would say that your observation is correct.
For a clear understanding of this process, your best bet would be to read the JCA specification. It is a clearly and easily understandable spec.
IBM provides their JCA adapter which is deployed to JBoss. When JBoss deploys your MDB, an "activation specification" is passed to the IBM JCA. The IBM JCA then creates a managed connection factory for the MDB instances. Note that this is separate and distinct from any connection factories as configured in the JBoss server configuration.
Part of the activation spec is the number of JMS sessions. The IBM JCA creates and manages these sessions. The IBM JCA also create JMS message listeners on these sessions.
When a message is received, the IBM JCA creates a message driven context, requests an MDB instance from the JBoss managed instance pool, provides the message driven context to the MDB instance and calls the MDB onMessage() method.