I have Spring service, which is actually actor, it is received info, but I cant pass it to another Spring service, because injection fails.
#Service("mailContainer")
#Scope("prototype")
#Component
public class MailContainer extends UntypedActor {
private final LoggingAdapter LOG = Logging.getLogger(getContext().system(), this);
private Mail value;
private List<Mail> mailList = new ArrayList<Mail>();
private Integer size;
#Autowired
#Qualifier("springService")
private SpringService springService;
//#Autowired
public void setSpringService(SpringService springService) {
this.springService = springService;
}
public MailContainer(Mail value) {
this.value = value;
}
#Override
public void onReceive(Object message) throws Exception {
// LOG.debug("+ MailContainer message: {} ", message);
if (message instanceof Mail) {
value = (Mail) message;
System.out.println("MailContainer get message with id " + value.getId());
System.out.println("With time " + value.getDateSend());
//getSender().tell(value, getSelf()); //heta uxarkum
//this.saveIt(value);
springService.add(value);
}
}
and second service
#Service("springService")
//#Component
#Scope("session")
public class SpringService {
private List<Mail> mailList = new ArrayList<Mail>();
public void add(Mail mail) {
System.out.println("Saving mail from Spring " +mail.getId());
mailList.add(mail);
}
public List<Mail> getMailList() {
return mailList;
}
}
Spring config, this is from akka spring example
#Configuration
//#EnableScheduling
//EnableAsync
#ComponentScan(basePackages = {"com"}, excludeFilters = {
#ComponentScan.Filter(Configuration.class)})
//#ImportResource("classpath:META-INF/spring/spring-data-context.xml")
//#EnableTransactionManagement
//#EnableMBeanExport
//#EnableWebMvc
public class CommonCoreConfig {
// the application context is needed to initialize the Akka Spring Extension
#Autowired
private ApplicationContext applicationContext;
/**
* Actor system singleton for this application.
*/
#Bean
public ActorSystem actorSystem() {
ActorSystem system = ActorSystem.create("AkkaJavaSpring");
// initialize the application context in the Akka Spring Extension
SpringExtProvider.get(system).initialize(applicationContext);
return system;
}
}
So, how I can inject just another Spring service?????????
Based on our discussions, I think it is due to the way you create the MailContainer actor. You aren't using the SpringExtProvider and instead are using Props.create directly. This means that Spring doesn't get the opportunity to perform dependency injection on your new actor.
Try changing this code:
#Override
public void preStart() throws Exception {
System.out.println("Mail collector preStart: {} ");
getContext().actorOf(Props.create(MailContainer.class, result), "one");
}
to use the the SpringExtProvider like this:
#Override
public void preStart() throws Exception {
System.out.println("Mail collector preStart: {} ");
getContext().actorOf(SpringExtProvider.get(getContext().system()).props("mailContainer"), "one");
}
This way you are asking the Spring extension to create the new actor and inject any required dependecnies.
Related
I have a class annotated with #Component which is use to initialze application.yml config properties. Service classe is using configuration property. But sometime my Service class instance created before the Configuration class and I get null property value in service class, Its random not specific pattern.
Configuration Initializer class..
#Component
public class ConfigInitializer implements InitializingBean {
private static final Logger log = LoggerFactory.getLogger(ConfigInitializer.class);
#Autowired
ProxyConfig proxyConfig;
/*#PostConstruct
public void postConstruct(){
setProperties();
}
*/
#Override
public void afterPropertiesSet() {
setProperties();
}
private void setSystemProperties(){
log.debug("Setting properties...");
Properties props = new Properties();
props.put("PROXY_URL", proxyConfig.getProxyUrl());
props.put("PROXY_PORT", proxyConfig.getProxyPort());
System.getProperties().putAll(props);
}
}
#Component
#ConfigurationProperties(prefix = "proxy-config")
public static class ProxyConfig {
private String proxyUrl;
private String proxyPort;
public String getProxyUrl() {
return proxyUrl;
}
public void setProxyUrl(String proxyUrl) {
this.proxyUrl = proxyUrl;
}
public String getProxyPort() {
return proxyPort;
}
public void setProxyPort(String proxyPort) {
this.proxyPort = proxyPort;
}
}
Service Class..
#Service("receiverService")
public class ReceiverService {
private static final Logger logger = LoggerFactory.getLogger(ReceiverService.class);
private ExecutorService executorService = Executors.newSingleThreadExecutor();
#Autowired
public ReceiverService() {
initClient();
}
private void initClient() {
Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
String value = System.getProperty("PROXY_URL"); **//Here I am getting null**
logger.info("Values : " + value);
}
});
System.out.println("future.get() = " + future.get());
}
}
Above Service class get null values String value = System.getProperty("PROXY_URL")
When I use #DependsOn annotation on Service class, it works fine.
In my little knowledge, I know Spring does not have specific order of bean creation.
I want to know If I use #Configuration instead of #Component on ConfigInitializer class like below, Will spring initialize ConfigInitializer
class before other beans ?.
#Configuration
public class ConfigInitializer implements InitializingBean {
//code here
}
I have a service that sends message to rabbitmq and the consumer do some manipulation of the message and re-queue them.
I can successfully send to rabbitmq the initial message but the problem is i cannot resend to rabbitmq any consumed message if the message requires modifications.
#Service
public class MyService {
/**
* The template
*/
#Autowired
private AmqpTemplate amqpTemplate;
private final RabbitMQConfig config;
public void send(String message) {
try {
amqpTemplate.convertAndSend("ex", "r", message);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Then in my config i have setup:
#Bean
public ConnectionFactory connectionFactory() { /* working code */ }
#Bean
public Queue myQueue() { return new Queue("my-queue");
// etc...
#Bean
MessageListenerAdapter myListenerAdapter(MyListener listener) {
return new MessageListenerAdapter(listener, "listener");
}
#Bean
MyListener myListener() {
return new MyListener();
}
then...
public class MyListener {
public void receiveMessage(String message) {
// ... some code
// if message requires modification, then repush
new Repush().push(message);
}
}
I tried to create a new class with new but the "myService" always null
#Component
public class Repush {
#Autowired
private MyService myService;
public void push(String message) {
// myService is null at this point
}
}
Don't use new for bean creation. Spring injects fields only in beans. Your MyListener is a bean. Just add Repush field with #Autowired annotation in this class.
public class MyListener {
#Autowired
private Repush repush;
public void receiveMessage(String message) {
// ... some code
// if message requires modification, then repush
repush.push(message);
}
}
If you declare myService as a bean in the application context as well as Repush as a bean you can then inject it into MyListener using #Autowired.
By creating Repush using new at point-in-time within the listener method, you are not getting a bean that is cognizant of the context you are in.
Scenario:
Junit for a microservice which listens to a queue and posts to an exchange in rabbitMQ after data extraction.
Issue:
RabbitListenerTestHarness is creating mock object for the Rabbit
Listener class alone, Actual objects are being instantiated for
Listeners Autowired components
I couldnt find a way to manually inject mock beans into the listener. This causes Junit to post the test messages to the actual queues configured in the microservice during Junit Execution.
Workaround: The only way I could use the rabbit-test project is to configure test exchange for posting the messages during Junit execution.
Query:
I wanted to understand, if there is any way better way of writing Junit for a Rabbit Listener. Also i wanted to understand if there is a way to maually inject mock objects to the Rabbit Listeners autowired components.
Sample code Snippet:
Rabbit Listener Class
#RabbitListener(id = "id", bindings = #QueueBinding(value = #Queue(value = "sampleQueue", durable = "true", autoDelete = "false"),key = "sampleRoutingKey", exchange = #Exchange(value = "sampleExchange", durable = "true", ignoreDeclarationExceptions = "true", type = EXCHANGE_TYPE)))
public void getMessageFromQueue(#Payload EventModel event) throws ListenerExecutionFailedException, JAXBException {
dataExporterService.exportDataAndPostToRabbit(event);
}
Service class
#Autowired
DataExtractorRepository dataExtractorRepository;
#Autowired
DataPublihserRepository dataPublisherRepo;
public void exportDataAndPostToRabbit(EventModel event) throws JAXBException {
dataPublisherRepo.sendMessageToExchange(dataExtractorRepository.extractOrderData(event), exchangeName, routingKeyValue);
}
DataPublihserRepository has rabbitTemplate internally Autowired. DataExtractorRepository connects to DB internally for retriving the message.
Test class
#Autowired
private RabbitListenerTestHarness harness;
#Autowired
private RabbitTemplate rabbitTemplate;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
DataExporterController = this.harness.getSpy("id");
}
#Test
public void shouldReceiveMessage() throws Exception {
LatchCountDownAndCallRealMethodAnswer answer = new LatchCountDownAndCallRealMethodAnswer(1);
doAnswer(answer).when(DataExporterController).getMessageFromQueue(any(EventModel.class));
rabbitTemplate.convertAndSend("sampleExchange", "sampleRoutingKey", createMessage());
assertTrue(answer.getLatch().await(10, TimeUnit.SECONDS));
verify(DataExporterController, times(1)).getMessageFromQueue(any(OrderEventsModel.class));
verify(orderDataExporterController, times(1)).getMessageFromQueue(any(OrderEventsModel.class));
}
private Message createMessage() {
String inputObject = "{\"id\":12345}";
MessageProperties props = MessagePropertiesBuilder.newInstance().setContentType(MessageProperties.CONTENT_TYPE_JSON).build();
return new Message(inputObject.getBytes(), props);
}
The harness is intended as a mechanism to verify that the listener received the data in an integration test. To unit test a listener, invoke its onMessage Method.
For example, using Mockito, given
public class MyListener {
#Autowired
private SomeService service;
#RabbitListener(id = "myListener", queues = "foo")
public void listen(Foo foo) {
this.service.process(foo);
}
}
and
public interface SomeService {
void process(Foo foo);
}
then
#RunWith(SpringRunner.class)
public class So53136882ApplicationTests {
#Autowired
private RabbitListenerEndpointRegistry registry;
#Autowired
private SomeService service;
#Test
public void test() throws Exception {
SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) this.registry
.getListenerContainer("myListener");
ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener();
Message message = MessageBuilder.withBody("{\"bar\":\"baz\"}".getBytes())
.andProperties(MessagePropertiesBuilder.newInstance()
.setContentType("application/json")
.build())
.build();
listener.onMessage(message, mock(Channel.class));
verify(this.service).process(new Foo("baz"));
}
#Configuration
#EnableRabbit
public static class config {
#Bean
public ConnectionFactory mockCf() {
return mock(ConnectionFactory.class);
}
#Bean
public MessageConverter converter() {
return new Jackson2JsonMessageConverter();
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(mockCf());
factory.setMessageConverter(converter());
factory.setAutoStartup(false);
return factory;
}
#Bean
public MyListener myListener() {
return new MyListener();
}
#Bean
public SomeService service() {
return mock(SomeService.class);
}
}
}
Notice that the container factory does not start the listener container.
For testing publishing, inject a mock RabbitOperations which is implemented by RabbitTemplate.
For example, given
public class SomeServiceImpl implements SomeService {
#Autowired
private RabbitOperations rabbitOperations;
#Override
public void process(Foo foo) {
this.rabbitOperations.convertAndSend(
"someExchange", "someRoutingKey", new Foo(foo.getBar().toUpperCase()));
}
}
and
#Bean
public SomeService service() {
return new SomeServiceImpl();
}
#Bean
public RabbitOperations rabbitTemplate() {
return mock(RabbitOperations.class);
}
then
#Test
public void test() throws Exception {
SimpleMessageListenerContainer container = (SimpleMessageListenerContainer) this.registry
.getListenerContainer("myListener");
ChannelAwareMessageListener listener = (ChannelAwareMessageListener) container.getMessageListener();
Message message = MessageBuilder.withBody("{\"bar\":\"baz\"}".getBytes())
.andProperties(MessagePropertiesBuilder.newInstance()
.setContentType("application/json")
.build())
.build();
listener.onMessage(message, mock(Channel.class));
verify(this.rabbitTemplate).convertAndSend("someExchange", "someRoutingKey", new Foo("BAZ"));
}
I have an issue/problem with CDI in the next scenario:
Initializator is injected in the ServletContextListener. But after some other "steps" the method startup is invoked:
#WebListener
public class ContextListener implements ServletContextListener {
#Inject
private Initializator initializator;
public void contextInitialized(ServletContextEvent event) {
ServletContext servletContext = (ServletContext) event.getSource();
String contextPath = ((ServletContext) event.getSource()).getContextPath();
String serverName = servletContext.getInitParameter("SERVER_NAME");
initializator.startup(serverName);
System.out.println("ServletContext " + contextPath + " stated.");
}
public void contextDestroyed(ServletContextEvent event) {
String contextPath = ((ServletContext) event.getSource()).getContextPath();
System.out.println("ServletContext " + contextPath + " stopped.");
}
}
The repository is successful injected in the initializator:
public class Initializator {
#Inject
private ChannelRepository repo;
public String serverName;
public void startup(String aServerName) {
this.serverName = aServerName;
initAll();
}
private void initAll() {
List<Channel> channels = repo.getChannels();
for (Channel channel : channels) {
channel.start();
}
}
}
The repository retrieves the data and instantiates channels:
public class ChannelRepository {
public List<Channel> getChannels() {
List<Channel> channels = new ArrayList<Channel>();
// ...some db access via jdbc (or jpa)
channels.add(new Channel("dummy", 8080));
return channels;
}
}
The channel needs a Logger:
public class Channel extends Thread {
#Inject
private Logger logger;
public String name;
public int port;
public Channel(String aName, int aPort) {
this.name = aName;
this.port = aPort;
}
#Override
public void run() {
logger.log("Channel " + name + " is running in port " + port);
// ...some other things to do
}
}
How to avoid the manual creation of Channel instances?
The problem is done because the startup method in Initializator is invoked after the instance construction.
How to manage this type of "deferred" injections?
Avoiding the manual creation of instances of Channel with
new Channel()
is fairly easy.
First we need a default constructor in class Channel and setters for channels attributes.
Then you must add this to your Channel Repo
#Inject
Instances<Channel> channelInstances;
and in your repo method change from
channels.add(new Channel("dummy", 8080));
to
Channel channel = channelInstances.get();
channel.setPort(8080);
channel.setName("dummy");
channels.add(channel);
A small hint:
If it is possible to do, don't let Channel extend Thread, but do the following
final Channel channel = channelInstances.get();
channel.setPort(8080);
channel.setName("dummy");
channels.add(new Thread() {
#Override
public void run() {
channel.doTheChannelStuff();
channelInstances.destroy(channel);
}
}
Why should you do this:
Under some circumstances a memory leak will be introduced when doing it in the way you are trying to use it. (Had a similiar issue at work) This has something to do with dependent scoped (default) dependencies and "manual" creation of new instances.
I have a little problem. I think this is typical question. However, I can't find good example. My application is using Jersey. And I want to test controller by client as test. Controller has private field - StudentService. When I debug test I see, that field is null. This leads to error. And I need to inject this field. I tried this:
My Controller
#Path("/student")
#Component
public class StudentResourse {
#Autowired
private StrudentService service; // this field Spring does not set
#Path("/getStudent/{id}")
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Student getStudent(#PathParam("id") long id) {
return service.get(id);
}
}
My JUnit test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:config.xml")
#TestExecutionListeners({ DbUnitTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class StudentResourseTest extends JerseyTest {
private static final String PACKAGE_NAME = "com.example.servlet";
private static final String FILE_DATASET = "/data.xml";
#Autowired
private StudentService service; // this field is setted by Spring, but I do not need this field for test
public StudentResourseTest() {
super(new WebAppDescriptor.Builder(PACKAGE_NAME).build());
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new HTTPContainerFactory();
}
#Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("restful.server.resource")
.contextParam("contextConfigLocation",
"classpath:/config.xml").contextPath("/")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class).build();
}
#Test
#DatabaseSetup(FILE_DATASET)
public void test() throws UnsupportedEncodingException {
ClientResponse response = resource().path("student").path("getStudent")
.path("100500").accept(MediaType.APPLICATION_XML)
.get(ClientResponse.class);
Student student = (Student) response.getEntity(Student.class);
} }
I guees, that problem is in test class. Because, when I run my application not in test, I can directly request students and everything working fine. But when I test classes, internal field of Controller does not setted. How to fix this bug? Thanks for your answers.
This is in my config.xml
<context:component-scan base-package="com.example" />
<bean id="StudentResourse" class="com.example.servlet.StudentResourse">
<property name="service" ref="studentService" />
</bean>
<bean id="service" class="com.example.service.StudentServiceImpl" />
One issue may be that you're trying to configure your test application in constructor and in configure() method. Use one or another but not both because in this case your configure() method is not invoked and hence you may not be using SpringServlet and everything that is defined in this method.
Reference: https://github.com/jiunjiunma/spring-jersey-test and http://geek.riffpie.com/unit-testing-restful-jersey-services-glued-together-with-spring/
Idea is to get a hold of the application context inside jersey by using ApplicationContextAware interface. There after we can grab the exact bean already created by spring, in your case, StudentService. Below example shows a mocked version of the dependency, SampleService, used to test the resource layer apis.
Resource class delegating the processing to a service layer
#Component
#Path("/sample")
public class SampleResource {
#Autowired
private SampleService sampleService;
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path ("/{id}")
public Sample getSample(#PathParam("id") int id) {
Sample sample = sampleService.getSample(id);
if (sample == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return sample;
}
}
Service layer encapsulating business logic
#Service
public class SampleService {
private static final Map<Integer, Sample> samples = new HashMap<>();
static {
samples.put(1, new Sample(1, "sample1"));
samples.put(2, new Sample(2, "sample2"));
}
public Sample getSample(int id) {
return samples.get(id);
}
}
Unit test for the above resource
public class SampleResourceTest extends SpringContextAwareJerseyTest {
private SampleService mockSampleService;
// create mock object for our test
#Bean
static public SampleService sampleService() {
return Mockito.mock(SampleService.class);
}
/**
* Create our own resource here so only the test resource is loaded. If
* we use #ComponentScan, the whole package will be scanned and more
* resources may be loaded (which is usually NOT what we want in a test).
*/
#Bean
static public SampleResource sampleResource() {
return new SampleResource();
}
// get the mock objects from the internal servlet context, because
// the app context may get recreated for each test so we have to set
// it before each run
#Before
public void setupMocks() {
mockSampleService = getContext().getBean(SampleService.class);
}
#Test
public void testMock() {
Assert.assertNotNull(mockSampleService);
}
#Test
public void testGetSample() {
// see how the mock object hijack the sample service, now id 3 is valid
Sample sample3 = new Sample(3, "sample3");
Mockito.when(mockSampleService.getSample(3)).thenReturn(sample3);
expect().statusCode(200).get(SERVLET_PATH + "/sample/3");
String jsonStr = get(SERVLET_PATH + "/sample/3").asString();
Assert.assertNotNull(jsonStr);
}
}
SpringContextAwareJerseyTest
#Configuration
public class SpringContextAwareJerseyTest extends JerseyTest {
protected static String SERVLET_PATH = "/api";
final private static ThreadLocal<ApplicationContext> context =
new ThreadLocal<>();
protected String getResourceLocation() {
return "example.rest";
}
protected String getContextConfigLocation() {
return getClass().getName();
}
static private String getContextHolderConfigLocation() {
return SpringContextAwareJerseyTest.class.getName();
}
protected WebAppDescriptor configure() {
String contextConfigLocation = getContextConfigLocation() + " " +
getContextHolderConfigLocation();
Map<String, String> initParams = new HashMap<>();
initParams.put("com.sun.jersey.config.property.packages",
getResourceLocation());
initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "true");
return new WebAppDescriptor.Builder(initParams)
.servletClass(SpringServlet.class)
.contextParam(
"contextClass",
"org.springframework.web.context.support.AnnotationConfigWebApplicationContext")
.contextParam("contextConfigLocation", contextConfigLocation)
.servletPath(SERVLET_PATH) // if not specified, it set to root resource
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
protected final ApplicationContext getContext() {
return context.get();
}
#Bean
public static ContextHolder contextHolder() {
return new ContextHolder();
}
private static class ContextHolder implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context.set(applicationContext);
}
}
}
Using the above with jersey 1.8