I am implementing WebSocket in spring 4.0.5. The client(browser) keep a connection to the server. When my service layer update some record at the back end, I want to inform the client which subscribe to the particular topic.
I want my Controller to listen to my ApplicationEvent and publish it to the WebSocket client. Is this possible?
I have another listener implemented in my service layer and it can capture the event.
Is it true that the controller cannot listen to the ApplicationEvent?
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import com.geneoz.service.event.MessageEvent;
#Controller
public class ApplicationEventObservorController implements ApplicationListener<MessageEvent> {
private static Logger logger = Logger.getLogger(ApplicationEventObservorController.class);
private SimpMessagingTemplate template;
#Autowired
public ApplicationEventObservorController(SimpMessagingTemplate template) {
logger.debug("Initialising ApplicationEventObservorController");
this.template = template;
}
#Override
public void onApplicationEvent(MessageEvent event) {
logger.debug("Captured event. " + event);
this.template.convertAndSend("/topic/update", event.toString());
}
}
Related
I want to create a daily background job to be executed by AEM.
I read an aem document and apache sling official site, and I thought I need two classes.
a service class that register the job to JobManager.
a consumer class that do the job.
So I tried these code, but my job was not executed.
service class
import org.apache.sling.event.jobs.JobManager;
import org.apache.sling.event.jobs.JobBuilder.ScheduleBuilder;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Component
public class MyJobService {
private static final Logger logger = LoggerFactory.getLogger(MyJobService.class);
#Reference
private JobManager jobManager;
public static final String JOB_TOPIC = "my/sample/jobtopic";
public void startScheduledJob() {
ScheduleBuilder scheduleBuilder = jobManager.createJob(JOB_TOPIC).schedule();
scheduleBuilder.hourly(9, 0); // execute daily at AM9:00
if (scheduleBuilder.add() == null) {
logger.error("myjobservice error");
}
}
}
job consumer class
import org.apache.sling.event.jobs.Job;
import org.apache.sling.event.jobs.consumer.JobConsumer;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#Component(
immediate = true,
service = JobConsumer.class,
property = {
JobConsumer.PROPERTY_TOPICS + "=my/sample/jobtopic"
}
)
public class MyJobConsumer implements JobConsumer {
private static final Logger logger = LoggerFactory.getLogger(MyJobConsumer.class);
#Override
public JobResult process(Job job) {
String topic = job.getTopic();
logger.info("this message is from myjobconsumer. topic is " + topic);
return JobResult.OK;
}
}
Do I need another class or some configurations? Does My code have something wrong?
If you annotate a method with #Activate it will be called when the component starts.
#Activate
public void startScheduledJob()
I guess you want your job to run on startup.
Another option would be to let MyJobService be a servlet and call it from outside.
I am learning about ActiveMQ, and so far I have made a simple Spring Boot producer+consumer application (call it App1 for the purposes of this question) that communicates with a local instance of ActiveMQ and everything works as expected.
Now I am trying to run a different Spring Boot application (on the same computer but after ensuring App1 is not running) that has only a consumer (no producer), but when I start up this application, the messages in the queue (that I put using a modified App1 in which I removed the consumer portion of the application) do not get picked up. In App1, as soon as the message was published, the consumer printed out the system.out print statement, but not so in this consumer-only application. Below is my listener component class:
package com.demo.listener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
#Component
public class Consumer {
#JmsListener(destination = "testqueue")
public void consume(String message) {
System.out.println("Picked up message: " + message);
}
}
What changes would I need to make in order achieve the desired behavior?
App1 application.properties file:
spring.activemq.in-memory=false
spring.activemq.pool.enabled=false
server.port=9000
activemq.broker-url=tcp://localhost:61616
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration, org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
security.basic.enabled=false
management.security.enabled=false
App1 JmsConfig class
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.command.ActiveMQQueue;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.core.JmsTemplate;
#Configuration
public class JmsConfig {
#Value("${activemq.broker-url}")
private String brokerUrl;
#Bean
public Queue queue() {
return new ActiveMQQueue("testqueue");
}
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
factory.setBrokerURL(brokerUrl);
return factory;
}
#Bean
public JmsTemplate jmsTemplate() {
return new JmsTemplate(activeMQConnectionFactory());
}
}
App1 Producer class
import javax.jms.Queue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/rest/publish")
public class ProducerResource {
#Autowired
JmsTemplate jmsTemplate;
#Autowired
Queue queue;
#GetMapping("/{message}")
public String publishMessage(#PathVariable("message") final String message) {
jmsTemplate.convertAndSend(queue, message);
return "Published successfully";
}
}
App1 consumer class is the same class I used in the consumer only application (listed above).
For your consumer app, you do need to add Pool connection factory & JMS message listener factory for your consumer JMStemplate to start receiving messages.
#Configuration
#EnableJms
public class ConsumerConfig {
#Value("${activemqbrokerurl}")
private String brokerUrl;
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(brokerUrl);
return activeMQConnectionFactory;
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(activeMQConnectionFactory());
factory.setConcurrency("{#setDesiredConcurrency}");
return factory;
}
}
Spring's MessagListenerContainer should be used for message consumption. This provides all the power of MDBs - efficient JMS consumption and pooling of the message listeners - but without requiring a full EJB container.You can use the activemq-pool org.apache.activemq.pool.PooledConnectionFactory for efficient pooling of the connections and sessions for your collection of consumers, or you can use the Spring JMS org.springframework.jms.connection.CachingConnectionFactory to achieve the same effect.
You can read more about it here
I've implemented Spring RESTful Web Services with OAuth2 in Spring Boot with id token:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
#Configuration
public class ResourceServerConfig {
private static final String SERVER_RESOURCE_ID = "oauth2-server";
private static final Logger LOG = LoggerFactory.getLogger(ResourceServerConfig.class);
private static InMemoryTokenStore tokenStore = new InMemoryTokenStore();
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Configuration
#EnableResourceServer
#Order(2)
protected class ResourceServer extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
LOG.info("ResourceServerSecurityConfigurer");
resources.tokenStore(tokenStore).resourceId(SERVER_RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
LOG.info("HttpSecurity");
http.anonymous().disable().
requestMatchers().antMatchers("/v1/**").
and().authorizeRequests().antMatchers("/v1/**").authenticated();
}
}
#Configuration
#EnableAuthorizationServer
#Order(1)
protected class AuthConfig extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
LOG.info("AuthorizationServerEndpointsConfigurer");
endpoints.userDetailsService(userDetailsService)
.authenticationManager(authenticationManager).tokenStore(tokenStore).approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
LOG.info("ClientDetailsServiceConfigurer: {}", clients);
clients.inMemory().withClient("client").authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit", "client_credentials")
.authorities("ROLE_CLIENT").scopes("read").resourceIds(SERVER_RESOURCE_ID).secret("secret");
}
}
}
I'm using load balancer to route authentication and calls request, but id_token doesn't same between all servers since id_token is saved just in memory and when user calls an authentication has an id_token1 from server 1 and when calls a request perhaps the load balancer route to Server 2 and here doesn't exist id_token1. An opcion to solve this issue is using Redis (e.g) to save id_token, another option also can be using JWT. As I'm using WebSphere as server application I'd like use session id to keep id_token across all servers, I don't think this could be a good idea but still I want implement this solution, have you got any ideas how could implement this solution?
You already know your two options. If your idea is to make the application stateless, I would suggest JWT. However, it comes with a price of adding extra security to JWT so that you don't expose your token as everything will be on the client side. One main advantage of using JWT is that you don't have to worry about additional server/resources that you'll need to provide to redis server.
I know I should not be testing void methods like this, but I am just testing Mockito.doNothing() as of now with a simple example.
My Service class:
#Service
public class Service{
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscribtions(){
kafkaConsumer.unsubscribe();
}
}
My Test class:
#MockBean
private Consumer<String, String> kafkaConsumer;
#Test
public void testClearSubscriptions() {
Service service = new Service();
Mockito.doNothing().when(kafkaConsumer).unsubscribe();
service.clearSubscriptions();
}
The test keeps failing with a null pointer exception. When I debugged it, it goes into the clearSubscription method of the service class, and there on the line of kafkaConsumer.unsubscribe(), kafkaConsumer is null. But I mocked the consumer, why is it throwing null pointer exception and I should be skipping over that method, right?
Edit:
All the declarations of the class:
#Autowired
private Consumer<String, String> kafkaConsumer;
#Autowired
private Service2 service2;
private final Object lock = new Object();
private static Logger logger = LoggerFactory.getLogger(Service.class);
private HashMap<String, String> subscribedTopics = new HashMap<>();
Figured out what was wrong, I needed to auto wire the service
You are instantiating a new service Service service = new Service(); but from what I can see you are never injecting the mock bean into the new service.
Here is a sample of what I think you could do if you are using mockito only and dont need to instantiate a spring container (used a single class for ease of example dont do this in actual code):
package com.sbp;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#RunWith(MockitoJUnitRunner.class) // run with mockitos runner so annotations are processed
public class MyServiceTest {
public interface Consumer<T, R> {
public void unsubscribe();
}
#Service
public class KafkaConsumer implements Consumer<String, String> {
#Override
public void unsubscribe() {
}
}
#Service
public class MyService {
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscriptions() {
kafkaConsumer.unsubscribe();
}
}
#Mock // tell mockito that this is a mock class - it will instantiate for you
private Consumer<String, String> kafkaConsumer;
#InjectMocks // tell mockito to inject the above mock into the class under test
private MyService service = new MyService();
#Test
public void testClearSubscriptions() {
service.clearSubscriptions();
Mockito.verify(kafkaConsumer, Mockito.times(1)).unsubscribe();
}
}
If you need an example via Spring using MockBean or without and dependencies, let me know and I can post.
UPDATED: adding sample using spring junit runner and using spring boot's mockbean annotation
package com.sbp;
import com.sbp.MyServiceTest.TestContext.MyService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class) // run with spring
#SpringBootTest(classes = MyServiceTest.TestContext.class) // make it a spring boot test so #MockBean annotation is processed, provide a dummy test context class
public class MyServiceTest {
public interface Consumer<T, R> {
public void unsubscribe();
}
#Configuration
public static class TestContext {
#Service
public class KafkaConsumer implements Consumer<String, String> {
#Override
public void unsubscribe() {
}
}
#Service
public class MyService {
#Autowired
private Consumer<String, String> kafkaConsumer;
public void clearSubscriptions() {
kafkaConsumer.unsubscribe();
}
}
}
#MockBean // this will create a mockito bean and put it in the application context in place of the Kafka consumer bean defined in the TestContext class
private Consumer<String, String> kafkaConsumer;
#Autowired // inject the bean from the application context that is wired with the mock bean
private MyService myService;
#Test
public void testClearSubscriptions() {
myService.clearSubscriptions();
Mockito.verify(kafkaConsumer, Mockito.times(1)).unsubscribe();
}
}
To me, this appears to be just about the simplest possible spring integration example. I'm trying to learn from the si4demo. But when I run it, I get this exception:
Exception in thread "main"
org.springframework.messaging.MessageDeliveryException: Dispatcher has
no subscribers for channel 'application.inbox'.; nested exception is
org.springframework.integration.MessageDispatchingException:
Dispatcher has no subscribers
Where am I going wrong? Doesn't the defined flow create a subscription to the inbox channel?
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.messaging.MessageChannel;
#Configuration
#ComponentScan
#IntegrationComponentScan
public class App {
public static void main(String[] args) {
try (ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args)) {
final Gateway gateway = ctx.getBean(Gateway.class);
final String rs = gateway.send("hullo");
System.out.println(rs);
}
}
private static final String INBOX = "inbox";
#MessagingGateway(defaultRequestChannel = INBOX)
public interface Gateway {
String send(String msg);
}
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(INBOX)
.transform(p -> "world")
.get();
}
#Bean(name = INBOX)
public MessageChannel inbox() {
return new DirectChannel();
}
}
Looks like you have missed the main player - #EnableIntegraion:
Starting with version 4.0, the #EnableIntegration annotation has been introduced, to allow the registration of Spring Integration infrastructure beans (see JavaDocs). This annotation is required when only Java & Annotation configuration is used, e.g. with Spring Boot and/or Spring Integration Messaging Annotation support and Spring Integration Java DSL with no XML integration configuration.
http://docs.spring.io/spring-integration/docs/4.3.0.BUILD-SNAPSHOT/reference/html/overview.html#configuration-enable-integration