I am trying to work on a tibco JMS CDC product. I have issues setting up the configuration and could not find a solution to my problem.
import com.tibco.tibjms.TibjmsConnectionFactory;
import javax.jms.JMSException;
#Configuration
#EnableJms
public class TibcoBusConfiguration {
#Value("${ems.password}")
private String password;
#Value("${ems.port}")
private String port;
#Value("${ems.topic}")
private String queue;
#Value("${ems.server}")
private String server;
#Value("${ems.user}")
private String user;
#Bean(name = "tibjmsConnectionFactory")
public TibjmsConnectionFactory jmsConnectionFactory() throws javax.jms.JMSException {
final TibjmsConnectionFactory factory = new TibjmsConnectionFactory();
factory.setServerUrl(serverURL());
factory.setUserName(user);
factory.setUserPassword(password);
return factory;
}
#Bean
public JmsTemplate jmsTemplate(
#Autowired TibjmsConnectionFactory tibjmsConnectionFactory) throws JMSException {
final JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(jmsConnectionFactory());
jmsTemplate.setDefaultDestinationName(queue);
jmsTemplate.setExplicitQosEnabled(true);
jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);
jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
jmsTemplate.setSessionTransacted(false);
return jmsTemplate;
}
private String serverURL() {
return "tcp://" + server + ":" + port;
}
}
Unfortunately JmsTemplate only allows jakarta.jms.ConnectionFactory, how do I pass in the TibcoConnectionFactory, because casting is not allowed since the classes do not match. Is my understanding of this incorrect?
I have the following JAR's in my maven setup: tibjms.jar jakarta.jms-api-3.0 javax.jms-api-2.0
Thanks in Advance
Boot 3/Spring 6 moved from the javax to the jakarta namespace for JEE support - you either need to find a Tibco jar for JEE 9 or drop back to Boot 2.7/Spring 5.3.
Related
**Custom configuration for mail sender **
#Configuration
public class EmailConfig {
private EmailProperties emailProp;
#Autowired
ConstantRepository constantRepository;
public EmailConfig(EmailProperties emailProp) {
this.emailProp = emailProp;
}
#Bean
public JavaMailSender getJavaMailSender() {
Constants cons = constantRepository.findByConstantKeyAndStatus("DEFAULT_MAIL_ACCOUNT_CREDENTIAL",true);
String password = cons.getValue();
}
here I am trying to fetch the password from the database but the problem is while building the app it calls the repository which leads to failure as IP whitelisting issue it is getting error: unable to acquire JDBC connection.
How to stop these repo calls while building the app
You can use #Lazy annotation at the Configuration level. So that the beans will be created at runtime when requested for First-time.
#Configuration
#Lazy
public class EmailConfig {
private EmailProperties emailProp;
#Autowired
ConstantRepository constantRepository;
public EmailConfig(EmailProperties emailProp) {
this.emailProp = emailProp;
}
#Bean
public JavaMailSender getJavaMailSender() {
Constants cons = constantRepository.findByConstantKeyAndStatus("DEFAULT_MAIL_ACCOUNT_CREDENTIAL",true);
String password = cons.getValue();
}
Reference: https://www.baeldung.com/spring-lazy-annotation
public class RedisMessageListener implements MessageListener {
#Value("#{systemProperties['pub']}")
private String publisher;
#Autowired
private LoadMoedlsFromDB ld;
#Override
public void onMessage( final Message message, final byte[] pattern ) {
System.out.println( "Publisher flag::" + publisher);
ld.downloadModels();
if("false".equals(publisher)) {
System.out.println(message.toString());
}
}}
This outputs Publisher flag::null
But the same config works in other classes, is this because this is a Redis listener class
The RedisMessageListener must be a spring bean. I don't know your spring configuration but if classpath scanning is enabled maybe it is enough to add #Component to your class.
The error I'm getting has to do with parsing the properties before injecting in to the Test class. I end up with ${property.name} when the property is injected. However the configuration of the Test class seems very wrong considering there are nested dependencies.
Specific error: Caused by: java.net.URISyntaxException: Illegal character in authority at index 8: https://${sqs.endpoint}
I've got a config class to load a specific prop for a #Bean:
#Configuration
public class AWSConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(AWSConfig.class);
private #Value("${sqs.endpoint}") String endpoint;
#Bean(name = "awsClient")
#Primary
public AmazonSQSAsyncClient amazonSQSClient() {
AmazonSQSAsyncClient awsSQSAsyncClient
= new AmazonSQSAsyncClient();
awsSQSAsyncClient.setEndpoint(endpoint);
return awsSQSAsyncClient;
}
}
Here's where this #Bean is injected:
#Component
public class SqsQueueSender {
private static final Logger LOGGER = LoggerFactory.getLogger(SqsQueueSender.class);
private final QueueMessagingTemplate queueMessagingTemplate;
#Autowired
#Qualifier("awsClient")
AmazonSQSAsyncClient amazonSQSAsyncClient;
public SqsQueueSender(AmazonSQSAsync amazonSQSAsyncClient) {
this.queueMessagingTemplate = new QueueMessagingTemplate(amazonSQSAsyncClient);
}
//take advantage of convertAndSend to send POJOs in appropriate format
public void send(String queueName, String message) {
this.queueMessagingTemplate.convertAndSend(queueName, MessageBuilder.withPayload(message).build());
}
}
This all seems to work, at least the app starts up and prints logs from either location. I am unable to get a unit test running against this code though. I can't figure out how to set up the config correctly. Here's the latest iteration of the test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class SqsQueueSenderTest {
#Configuration
static class ContextConfiguration {
private #Value("${sqs.endpoint}") String endpoint;
#Bean(name = "awsClient")
#Primary
public AmazonSQSAsyncClient amazonSQSClient() {
AmazonSQSAsyncClient awsSQSAsyncClient
= new AmazonSQSAsyncClient();
awsSQSAsyncClient.setEndpoint(endpoint);
return awsSQSAsyncClient;
}
#Bean
public SqsQueueSender sqsQueueSender() {
SqsQueueSender sqsQueueSender = new SqsQueueSender(amazonSQSClient());
// set up the client
return sqsQueueSender;
}
}
#Autowired
SqsQueueSender sqsQueueSender;// = new SqsQueueSender(new AmazonSQSAsyncClient());
private static final Logger LOGGER = LoggerFactory.getLogger(SqsQueueSenderTest.class);
// attributes for in-memory sqs server
AmazonSQSClient client;
SQSRestServer server;
SQSRestServerBuilder sqsRestServerBuilder;
#Before
public void startup() {
LOGGER.info("Building in-memory SQS server");
this.server = sqsRestServerBuilder.withPort(9324).withInterface("localhost").start();
this.client = new AmazonSQSClient(new BasicAWSCredentials("x", "x"));
client.setEndpoint("http://localhost:9324");
client.createQueue("test");
LOGGER.info("Finished building in-memory SQS server");
}
#After
public void shutdown() {
LOGGER.info("Stopping in-memory SQS server");
server.stopAndWait();
LOGGER.info("Finished stopping in-memory SQS server");
}
#Test
public void testSending() {
LOGGER.info("~~~~~~~~~~~~~");
sqsQueueSender.send("test", "new message");
LOGGER.info("The current queues are" + client.listQueues().toString());
LOGGER.info("~~~~~~~~~~~~~");
}
}
Joe ,first of all put your connection properties in a resource for testing:
src/test/resouces/test.properties
Then add this to the Test class definition:
#PropertySource(
value={"classpath:test.properties"},
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class SqsQueueSenderTest {
And finally in your Configuration class add this bean:
#Configuration static class ContextConfiguration {
#Bean
public static PropertySourcesPlaceholderConfigurer properties() throws Exception {
return new PropertySourcesPlaceholderConfigurer();
}
}
Dont forget to place to place 'sqs.endpoint' url in your properties file.
This in my opinion is one of the cleaner ways of injecting your properties into the test class.
I am new to Spring Integration and to Google Cloud Message. XmppConnectionFactoryBean is created successfully and I can autowire a XmppConnection in my service class.
#Configuration
class XmppConfig {
#Value("${gcm.sender_id}")
private String senderId;
#Value("${gcm.api_key}")
private String apiKey;
#Value("${gcm.host}")
private String host;
#Value("${gcm.port}")
private int port;
#Bean
public ConnectionConfiguration connectionConfiguration() {
ConnectionConfiguration connectionConfig = new ConnectionConfiguration(host, port);
connectionConfig.setSecurityMode(SecurityMode.enabled);
connectionConfig.setReconnectionAllowed(true);
connectionConfig.setRosterLoadedAtLogin(false);
connectionConfig.setSendPresence(false);
connectionConfig.setSocketFactory(SSLSocketFactory.getDefault());
return connectionConfig;
}
#Bean
public XmppConnectionFactoryBean xmppConnectionFactoryBean() {
XmppConnectionFactoryBean connectionFactoryBean = new XmppConnectionFactoryBean();
connectionFactoryBean.setUser(senderId);
connectionFactoryBean.setPassword(apiKey);
connectionFactoryBean.setConnectionConfiguration(connectionConfiguration());
return connectionFactoryBean;
}
}
Service class:
class MyServiceImpl implements MyService {
#Autowired
private XmppConnection xmppConnection;
}
Is that the right approach? How can I send an XMPP message to GCM? Should I use XmppConnection directly or some Spring messaging abstraction?
UPDATE
Created a MessageHandler and defined bean names.
#Bean(name = "xmppConnection")
public XmppConnectionFactoryBean xmppConnectionFactoryBean() {
XmppConnectionFactoryBean connectionFactoryBean = new XmppConnectionFactoryBean();
connectionFactoryBean.setUser(senderId);
connectionFactoryBean.setPassword(apiKey);
connectionFactoryBean.setConnectionConfiguration(connectionConfiguration());
return connectionFactoryBean;
}
#Bean(name = "gcmChannel")
public MessageChannel messageChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "gcmChannel")
public MessageHandler messageHandler() {
return new ChatMessageSendingMessageHandler();
}
#Autowired
#Qualifier("gcmChannel")
private MessageChannel messageChannel;
Of course, it would be better to use a specific Spring Integration Adapter on the matter. It is ChatMessageSendingMessageHandler.
And we right now in the merging phase for the Extensions support for that adapter: https://github.com/spring-projects/spring-integration/pull/1745. So, with the next Spring Integration 4.3 version you will have more GCM support there.
Right now as a workaround you have to create an XMPP message with GCM extension manually and send it to the channel for that ChatMessageSendingMessageHandler.
I'm writing a client/server app and configuring it with Spring.
My client interface handles marshalling requests to the server and handling the responses.
At the moment, I have a factory that looks something like:
public class ClientFactory {
private ApplicationContext ctx;
public ClientFactory(){
ctx = new AnnotationConfigApplicationContext(MyConfig.class);
}
public MyClient(String host, int port){
MyClient client = ...
// create a connection to the server
return client;
}
}
Now, MyClient has a bunch of dependencies that I would like to inject, so I would like to create the MyClient instance using Spring and use #Inject annotations to inject the dependencies.
How do I pass the host/port as configuration metadata into the Spring configuration? If I can't what is the recommended alternative. I could do all the wiring myself, but then that is what Spring is for.
Jeff
You should check configuration part of the spring reference. For example you can create beans like this with spring 3.x.
#Configuration
// spring config that loads the properties file
#ImportResource("classpath:/properties-config.xml")
public class AppConfig {
/**
* Using property 'EL' syntax to load values from the
* jetProperties value
*/
private #Value("#{jetProperties['jetBean.name']}") String name;
private #Value("#{jetProperties['jetBean.price']}") Long price;
private #Value("#{jetProperties['jetBean.url']}") URL url;
/**
* Create a jetBean within the Spring Application Context
* #return a bean
*/
public #Bean(name = "jetBean")
JetBean jetBean() {
JetBean bean = new JetBeanImpl();
bean.setName(name);
bean.setPrice(price);
bean.setUrl(url);
return bean;
}
}
I solved this using a static configuration class.
public class ClientFactory {
private ApplicationContext ctx;
public ClientFactory(){
ctx = new AnnotationConfigApplicationContext(MyConfig.class,ServerConfig.class);
}
public MyClient(String host, int port){
MyClient client = ...
// create a connection to the server
return client;
}
#Data
#AllArgsConstructor
public static class ServerDetails{
private int port;
private String host;
}
#Configuration
public static class ServerConfig{
static String host;
static int port;
#Bean
public void serverDetails(){
return new ServerDetails(host, port);
}
}
}
It feels very clunky though. Is there a better way?